Pages

Showing posts with label code samples. Show all posts
Showing posts with label code samples. Show all posts

Friday, June 26, 2020

Build latest libvips

As mentioned in the libvips README, you can easily compile libvips if you need a newer version than is provided by your distribution. For example,
FROM ubuntu:18.04

ENV VIPS_VERSION=8.9.2
RUN set -o errexit -o nounset && \
    wget --no-verbose https://github.com/libvips/libvips/releases/download/v${VIPS_VERSION}/vips-${VIPS_VERSION}.tar.gz && \
    tar -xf /vips-${VIPS_VERSION}.tar.gz && \
    rm vips-${VIPS_VERSION}.tar.gz && \
    cd vips-${VIPS_VERSION} && \
    ./configure && \
    make && \
    make install && \
    ldconfig && \
    cd .. && \
    rm --recursive vips-${VIPS_VERSION}

But if you want to save some time recompiling (or want a deb for any other reason), you can modify the packaging to use the version you compiled instead. Here's an example with Ubuntu.
# docker build -t vips-deb .
# docker run --rm -v $PWD/debs:/debs vips-deb

FROM ubuntu:18.04

RUN mkdir /debs

WORKDIR /build

RUN set -o errexit -o nounset \
    && apt-get update --yes \
    && apt-get install --no-install-recommends --no-install-suggests --yes \
        build-essential \
        ca-certificates \
        devscripts \
        equivs \
        wget \
    && rm --recursive --force /var/lib/apt/lists/*

# use the Debian version that matches the base image from https://launchpad.net/ubuntu/+source/vips
ENV DEBIAN_VIPS_VERSION=8.4.5-1build1
ENV VIPS_VERSION=8.9.2
RUN set -o errexit -o nounset \
    && wget --no-verbose https://github.com/libvips/libvips/releases/download/v${VIPS_VERSION}/vips-${VIPS_VERSION}.tar.gz \
    && tar -xf vips-${VIPS_VERSION}.tar.gz \
    && rm vips-${VIPS_VERSION}.tar.gz \
    && cd vips-${VIPS_VERSION} \
    && wget --no-verbose https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/vips/${DEBIAN_VIPS_VERSION}/vips_${DEBIAN_VIPS_VERSION}.debian.tar.xz \
    && tar -xf vips_${DEBIAN_VIPS_VERSION}.debian.tar.xz \
    && rm vips_${DEBIAN_VIPS_VERSION}.debian.tar.xz \
    && cd debian \
    && EMAIL=john@example.com NAME="John Doe" dch -v ${VIPS_VERSION}-1 "Update to version ${VIPS_VERSION}" \
    && sed -Ei 's/^# deb-src /deb-src /' /etc/apt/sources.list \
    && apt-get update --yes \
    # don't install Python bindings (this will be supplied by pyvips)
    && sed -Ei '/Package: python-vipscc/,/^$/d' control \
    && mk-build-deps -t 'apt-get -y -o Debug::pkgProblemResolver=yes --no-install-recommends' -i control \
    && debuild -i -us -uc -bCMD cp /build/*.deb /debs \
    && rm --recursive --force /var/lib/apt/lists/*

CMD cp --force /build/*.deb /debs
One thing to note, as the Pyvips README says
If you have the development headers for libvips installed and have a working C compiler, this module will use cffi API mode to try to build a libvips binary extension for your Python. If it is unable to build a binary extension, it will use cffi ABI mode instead and only needs the libvips shared library. This takes longer to start up and is typically ~20% slower in execution.
You can confirm whether it is running in ABI mode after installing libvips by running this command
echo -e 'import logging\nlogging.basicConfig(level=logging.DEBUG)\nimport pyvips' | python3
Example ABI Mode Output
DEBUG:pyvips:Binary module load failed: No module named '_libvips'
DEBUG:pyvips:Falling back to ABI mode
DEBUG:pyvips:Loaded lib <cffi.api._make_ffi_library.<locals>.FFILibrary object at 0x7f6c56754978>
DEBUG:pyvips:Loaded lib <cffi.api._make_ffi_library.<locals>.FFILibrary object at 0x7f6c56754860>
DEBUG:pyvips:Inited libvips
Example Non-ABI Mode Output
DEBUG:pyvips:Loaded binary module _libvips
DEBUG:pyvips:Module generated for libvips 8.9
DEBUG:pyvips:Linked to libvips 8.9
DEBUG:pyvips:Inited libvips

Sunday, May 3, 2020

Blogger Syntax Highlighting (Again)

I was working on a blog post with Kotlin and noticed SyntaxHighlighter had no brush for Kotlin, not even V4.  Given that, and the popularity of highlight.js, I've decided to switch to that. You just gotta add something like this in the <header> of your Blogger template.
<!--<link href='//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/styles/default.min.css' rel='stylesheet'/>-->
<link href='//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/styles/androidstudio.min.css' rel='stylesheet'/>
<!--<link href='//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/styles/atom-one-dark.min.css' rel='stylesheet'/>-->
<!--<link href='//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/styles/agate.min.css' rel='stylesheet'/>-->
<!--<link href='//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/styles/darcula.min.css' rel='stylesheet'/>-->
<!--<link href='//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/styles/rainbow.min.css' rel='stylesheet'/>-->
<!--<link href='//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/styles/railscast.min.css' rel='stylesheet'/>-->
<!--<link href='//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/styles/solarized-dark.min.css' rel='stylesheet'/>-->
<!--<link href='//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/styles/zenburn.min.css' rel='stylesheet'/>-->
<script src='//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/highlight.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/autohotkey.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/bash.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/c-like.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/c.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/clojure.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/cpp.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/csharp.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/css.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/dockerfile.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/dos.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/fsharp.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/java.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/javascript.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/go.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/groovy.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/markdown.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/nsis.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/pgsql.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/plaintext.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/powershell.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/properties.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/python.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/ruby.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/scala.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/scss.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/sql.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/typescript.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/vbscript.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/xml.min.js'/>
<script charset='UTF-8' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.0/languages/yaml.min.js'/>
<scrpt>hljs.initHighlightingOnLoad();</script>

There are many other languages and styles available. And as with SyntaxHighlighter, you need to escape < with &lt; and > with &gt.
Then you put your code inside
<pre><code class="kotlin"></code>

</pre>
or
<pre><code class="lang-kotlin">

</code></pre>
or
<pre><code class="language-kotlin">

</code></pre>

Wednesday, July 15, 2015

Custom configuration script ASTs

Nikolay Totomanov asked on the Groovy mailing list how one could add a default constructor (if it doesn't already exist) to all classes. I realized there were no examples on the internet (that I could find anyway) of
  1. How to pass parameters into an AST in a configuration script
  2. How to use a custom AST in a configuration script
So I decided to try to remedy that with this post.

1. AST parameters in a configuration script
Let's say you wanted to do
@groovy.transform.TupleConstructor(includes=['foo'])
in a configuration script. How do you pass the includes? Use a map. The equivalent configuration script would be
withConfig(configuration) {
    ast(groovy.transform.TupleConstructor, includes:['foo'])
}

2. Custom AST in a configuration script
If you wanted to create your own AST and use it in a configuration script, I suggest looking at Groovy as a starting point. Here, I'll use groovy.transform.TupleConstructor and org.codehaus.groovy.transform.TupleConstructorASTTransformation as an example to solve Nikolay's problem. Here is the result
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
import org.codehaus.groovy.ast.ASTNode
import org.codehaus.groovy.ast.AnnotatedNode
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.ConstructorNode
import org.codehaus.groovy.ast.Parameter
import org.codehaus.groovy.ast.stmt.BlockStatement
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.transform.AbstractASTTransformation
import org.codehaus.groovy.transform.GroovyASTTransformation
import org.codehaus.groovy.transform.GroovyASTTransformationClass

@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
public class DefaultConstructorASTTransformation extends AbstractASTTransformation {
    public void visit(ASTNode[] nodes, SourceUnit source) {
        init(nodes, source)
        AnnotatedNode parent = (AnnotatedNode) nodes[1]
        if (parent instanceof ClassNode) {
            ClassNode cNode = (ClassNode) parent
            // doesn't already have a default constructor
            if (!cNode.getDeclaredConstructor(new Parameter[0])) {
                cNode.addConstructor(new ConstructorNode(
                    ACC_PUBLIC, new Parameter[0], cNode.EMPTY_ARRAY, new BlockStatement()))
            }
        }
    }
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@GroovyASTTransformationClass("DefaultConstructorASTTransformation")
public @interface DefaultConstructor {}

withConfig(configuration) {
    ast(DefaultConstructor)
}

Sunday, September 8, 2013

NSIS SelectSection

So, I haven't seen a working example of how Sections.nsh's SelectSection could be used (at least not one that worked for me anyway).  So here's an example based on what I did in the installer for Meld.  The goal was to include some default sections if the install was silent.
!include "Sections.nsh"
Section "Foo" foo
    ; install stuff here
SectionEnd
Section "un.foo" unfoo
     ; uninstall stuff here
SectionEnd
Function .onInit
     IfSilent isSilent notSilent
     isSilent:
     !insertMacro SelectSection foo
     !insertMacro SelectSection unfoo
     notSilent:
FunctionEnd

Thursday, April 25, 2013

Hadoop Writing Bytes

There are times where you might want to write bytes directly to HDFS.  Maybe you're writing binary data.  Maybe you're writing data with varying encodings.  In our case, we were doing both (depending on profile) and were trying to use MultipleOutputs to do so.  We discovered that there was no built-in OutputFormat that supported bytes, nor was there any examples on the web of how to do this with the new API. Granted, it's not overly complicated, but to save you a little time, here's what I came up with.

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.BytesWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.compress.CompressionCodec;
import org.apache.hadoop.io.compress.GzipCodec;
import org.apache.hadoop.mapreduce.RecordWriter;
import org.apache.hadoop.mapreduce.TaskAttemptContext;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.ReflectionUtils;

import java.io.DataOutputStream;
import java.io.IOException;


public class BytesValueOutputFormat extends FileOutputFormat<NullWritable, BytesWritable> {

    @Override
    public RecordWriter<NullWritable, BytesWritable> getRecordWriter(TaskAttemptContext taskAttemptContext) throws IOException, InterruptedException {
        Configuration conf = taskAttemptContext.getConfiguration();
        boolean isCompressed = getCompressOutput(taskAttemptContext);
        CompressionCodec codec = null;
        String extension = "";
        if (isCompressed) {
            Class<? extends CompressionCodec> codecClass = getOutputCompressorClass(taskAttemptContext, GzipCodec.class);
            codec = ReflectionUtils.newInstance(codecClass, conf);
            extension = codec.getDefaultExtension();
        }
        Path file = getDefaultWorkFile(taskAttemptContext, extension);
        FileSystem fs = file.getFileSystem(conf);
        if (!isCompressed) {
            FSDataOutputStream fileOut = fs.create(file, false);
            return new ByteRecordWriter(fileOut);
        } else {
            FSDataOutputStream fileOut = fs.create(file, false);
            return new ByteRecordWriter(new DataOutputStream(codec.createOutputStream(fileOut)));
        }
    }

    protected static class ByteRecordWriter extends RecordWriter<NullWritable, BytesWritable> {
        private DataOutputStream out;

        public ByteRecordWriter(DataOutputStream out) {
            this.out = out;
        }

        @Override
        public void write(NullWritable key, BytesWritable value) throws IOException {
            boolean nullValue = value == null;
            if (!nullValue) {
                out.write(value.getBytes(), 0, value.getLength());
            }
        }

        @Override
        public void close(TaskAttemptContext taskAttemptContext) throws IOException, InterruptedException {
            out.close();
        }
    }

}

Here's an example usage with MultipleOutputs
Instantiation
MultipleOutputs<NullWritable, BytesWritable> multipleOutputs = new MultipleOutputs<NullWritable, BytesWritable>(context);
Writing
byte[] bytesToWrite = someAppLogic();
MultipleOutputs.write(NullWritable.get(), new BytesWritable(bytesToWrite), fileName);

And of course, since it's like any other OutputFormat, it can also work with LazyOutputFormat if desired (as well as just about anything else you might choose to do with an OutputFormat).
LazyOutputFormat.setOutputFormatClass(job, BytesValueOutputFormat.class);

In our case, this the last step in our sequence of Hadoop jobs so we had no further need for the key. One could conceive of situations in which further manipulation is needed. In such cases, you could attempt some sort of delimited binary (to separate the key from the value), but it might be easier to just keep it all as text and use Common Codec's Base64 to pass the bytes value between jobs.

Saturday, June 2, 2012

Ubuntu 12.04 & Eclipse

I use Ubuntu at work for development, mostly with Jetbrains IntelliJ IDEA these days, I was surprised when I tried to fire up Eclipse for the first time in a while that I couldn't do so.  The error I was getting was
An error has occurred. See the log file /home/wittk/.eclipse/org.eclipse.platform_3.7.0_155965261/configuration/1338578313816.log
Here's an excerpt from the log mentioned in the message:
java.lang.UnsatisfiedLinkError: Could not load SWT library. Reasons: 
 no swt-gtk-3740 in java.library.path
 no swt-gtk in java.library.path
 Can't load library: /home/wittk/.swt/lib/linux/x86/libswt-gtk-3740.so
 Can't load library: /home/wittk/.swt/lib/linux/x86/libswt-gtk.so

The solution is to link the swt library files to the location it is looking.  This solution was mentioned here.  Just run the commands below.
cd ~/.swt/lib/linux/x86*
for file in /usr/lib/jni/libswt-*3740.so; do ln -s ${file}; done

Friday, December 17, 2010

Maven 3 & profiles.xml

There are some pretty cool things in Maven 3 (although mixins and global dependency exclusions have been tabled until 3.1). Matt Raible talks about some of them here. Significant points include dramatically increased performance (50% to 400% faster), unspecified plugin versions will pull the latest version and not snapshot versions (though it's best to be explicit about plugin versions), also Sonatype has developed Maven Shell and polyglot Maven to work with Maven 3.

There are, however, some compatibility concerns when moving from Maven 2.x. And not all plugins work yet. (Most notably they're refactoring the Site Plugin so it's not completely working yet). But the Maven team intends the new release to be usable as a drop-in replacement for Maven 2 (though this won't be the case for Maven 3.1).

Most changes for compatibility seemed pretty trivial. The biggest thing I see preventing Maven 3 from being a drop-in replacement for Maven 2 was their decision to remove support for profiles.xml. This was documented in MNG-4060, though not much discussed there. A few people have complained about this already here and it was discussed a little here. I haven't seen much justification for this, other than it's supposed to be difficult to test. Though I'm not sure why it'd be much harder to test than the use of profiles in the pom.xml. But the Maven team seems for whatever reason fairly committed to this idea, despite one of their committers (Milos Kleint) disagreeing with their position.

People use profiles for a variety of things. Where I work, they are commonly used for environment settings. For example, which version of a particular web service or database to use in prod, qa, etc. But also for which version of that service or database to use for a particular developer's sandbox (another common difference between developers is their log level). These typically use Maven filtering in conjunction with external profiles to accomplish this. These profile properties are also kept in our SCM so if something used by all developer's sandbox, it is easily changeable and transparent to all developers on the project. It is currently impossible to run Maven 3 in this way.

We are not entirely without options when it comes to addressing this, but none of the solutions in my opinion is as nice as the stable, built-in profiles.xml feature. Recently, I've asked if there is some other mechanism I should be using to accomplish this. To date, I've not seen anyone fully explore this issue that I think is important for Maven 3 going forward. So that's what I did here. If you'd like any of the sample projects I created for this exploration, just drop me a line. Maybe I'll put them up on GitHub or something at some point. So without further ado, here are the options I've found and their pros & cons.

1. Fork Maven and put the feature back in
Maybe it's my cynical nature, but this was actually the first thing that came to my mind. But I don't know that very many people would feel comfortable running a patched version of Maven. Plus, I'd want to keep forking them to keep getting all the other goodies they add, which makes a lot of work for me. Then I thought of submitting a patch to Maven for this. But when I found out the decision was pretty deliberate and not simply a lack of resources, I backed off that idea.
Pros Cons
It's the way Maven should be A lot of work
Who's brave enough to use it?

2. Stick with Maven 2
Hey, there's nothing wrong with being old fashioned. There's strong logic to the "If it ain't broke, don't fix it" argument. Some people are even still happily on the 2.0.x branch rather than the 2.2.x branch.
Pros Cons
The least work of any other solution No Maven 3 goodies

3. Place all profiles properties in the POM
POMs in Maven 3 still use a <modelVersion>4.0.0</modelVersion>. There for you could put everything inside it like
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.foo</groupId>
  <artifactId>pomProfileTest</artifactId>
  <version>1.0</version>

  <properties>
    <!-- these are defaults, they can be overridden with a settings.xml -->
    <javaVersion>1.5</javaVersion>
    <junitVersion>4.8.2</junitVersion>
    <sourceEncoding>UTF-8</sourceEncoding>
    <resourceEncoding>UTF-8</resourceEncoding>
    <profile>dev</profile>
    <prop1>null</prop1>
    <prop2>null</prop2>
  </properties>

  <profiles>
    <profile>
      <id>developer1</id>
      <properties>
        <prop1>prop1Value</prop1>
        <prop2>prop2Value</prop2>
      </properties>
    </profile>
    <profile>
      <id>developer2</id>
      <properties>
        <prop1>anotherProp1Value</prop1>
        <prop2>anotherProp2Value</prop2>
      </properties>
    </profile>
  </profiles>
<!-- ... -->
</project>
This could then be invoked in the standard way:
mvn -P developer1 <goal>
Pros Cons
Simple to implement: scripts can continue to invoke Maven in the current way Clutters POM, particularly troublesome since the POM is also deployed.
Also works with Maven 2
Variations
1. Multiple pom.xml files could be checked into the project to cut down on the clutter inside the main pom.xml.

4. Place all an environment's (or developer's) settings in a single settings.xml
This seems to be Maven's official answer on the subject. For things that are common across many projects, this might be a decent solution. But for the many project specific settings (e.g. a db.url property), you'd have to make sure they are named uniquely across all projects so as not to conflict with each other. This makes for a real maintenance problem.
As one commenter in the mailing list noted, some developers have a fear of changing their settings.xml (even if they should be comfortable with this) and would prefer to simply change a few properties. But this should make us pause to make sure there's nothing we're using profiles.xml for when we should actually be using settings.xml.
Pros Cons
Maven's official solution Uniquely named properties causes maintenance issues
Also works with Maven 2 Changes not normally visible to all developers

5. Use separate settings.xml files
You can specify a different user settings file with
mvn -s path/someSettingsFile.xml <goal>
and create a file like
developer1.xml
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <profiles>
    <profile>
      <id>developer1</id>
      <properties>
        <!-- global properties -->
        <siteLocation>file://${user.dir}/site</siteLocation>

        <!-- project specific properties -->
        <prop1>prop1Value</prop1>
        <prop2>prop2Value</prop2>
      </properties>
      <!-- ... -->
    <profile>
  <profiles>
</settings>

Pros Cons
Little profile clutter in POM Massive clutter & overhead in profile settings file, as all your normal settings.xml things are now in each profile settings file
Checking all these settings files in allows developers to make sure they're all using and deploying to the same repos profile settings are machine-dependent since <localRepository/> is also in settings file.
Also works with Maven 2

6. Use the Properties Maven Plugin
The Properties Maven Plugin allows for properties files to be loaded (and saved) just as if you had used <properties/> in the pom.xml itself.
Pros Cons
Properties are cleanly externalized Plugin is considered an alpha version
Also works with Maven 2

Variations
1. Use profiles to select the properties file
This could be invoked in the standard way:
mvn -P developer1 <goal>
And your pom might look like
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.foo</groupId>
  <artifactId>propertiesProfileTest</artifactId>
  <version>1.0</version>

  <properties>
    <!-- these are defaults, they can be overridden with a settings.xml -->
    <javaVersion>1.5</javaVersion>
    <junitVersion>4.8.2</junitVersion>
    <sourceEncoding>UTF-8</sourceEncoding>
    <resourceEncoding>UTF-8</resourceEncoding>
    <profile>dev</profile>
    <prop1>null</prop1>
    <prop2>null</prop2>
  </properties>

  <profiles>
    <profile>
      <id>developer1</id>
      <properties>
        <profile>developer1.properties</profile>
      </properties>
    </profile>
    <profile>
      <id>developer2</id>
      <properties>
        <profile>developer2.properties</profile>
      </properties>
    </profile>
  </profiles>

  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>properties-maven-plugin</artifactId>
        <version>1.0-alpha-2</version>
        <executions>
          <execution>
            <phase>initialize</phase>
            <goals>
              <goal>read-project-properties</goal>
            </goals>
            <configuration>
              <files>
                <file>${project.basedir}/filters/${profile}</file>
              </files>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  <!-- ... -->
</project>
Pros Cons
Standard way for selecting profile POM now is cluttered with mapping profiles to properties files

2. Use a variable to select the properties file
In my sample, the invocation would be
mvn -Dprofile=developer1 <goal>
And the POM would be like
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.foo</groupId>
  <artifactId>propertiesProfileTest</artifactId>
  <version>1.0</version>

  <properties>
    <!-- these are defaults, they can be overridden with a settings.xml -->
    <javaVersion>1.5</javaVersion>
    <junitVersion>4.8.2</junitVersion>
    <sourceEncoding>UTF-8</sourceEncoding>
    <resourceEncoding>UTF-8</resourceEncoding>
    <profile>dev</profile>
    <prop1>null</prop1>
    <prop2>null</prop2>
  </properties>

  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>properties-maven-plugin</artifactId>
        <version>1.0-alpha-2</version>
        <executions>
          <execution>
            <phase>initialize</phase>
            <goals>
              <goal>read-project-properties</goal>
            </goals>
            <configuration>
              <files>
                <file>${project.basedir}/filters/${profile}.properties</file>
              </files>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  <!-- ... -->
</project>
Pros Cons
Little profile clutter in POM No standard way of selecting profile

Conclusion
I still really wish the Maven folks would change their mind on this. But until then (unless you're sticking with Maven 2 -- which might not be a bad idea at least for now because of plugin incompatibilities), the best option seems to me to use the Maven Properties Plugin, either with a property or with a profile. (Personally, I'm leaning to the profile option beacuse I think the fact it can be invoked in a standard way is worth the extra POM clutter). Though it is technically an alpha version, it seemed to work fine for me and I believe Maven's decision to remove support for profiles.xml will cause people to flock to this plugin, and therefore likely to only increase its stability. Of course, IDE support for this practice is another question.

Conclusion Part 2
I do feel obligated to mention that the practice of using Maven filtering for things like database URLs where I work is actually changing to use runtime properties instead. (Generally by building a property reader class using java.util.Properties to read different properties files based on the name passed with -Denv=<environmentName>). This has the advantage (besides working with Maven 3) of not requiring separate deployments just for environment differences or redeploys for changes to an environment property.
I still think removing profiles.xml support is bad since the intent was to keep backward compatibility, thus making pointing my M2_HOME to Maven 3 a bit painful when working on old and new projects. It also seems a bit strange that they removed support for profiles.xml when they but not for profiles. It was nice to be able to have those external to the POM.
However, for my (and probably the gentleman on Nabble also) use case, Maven filtering with profiles probably may not have been the right idea in the first place.

Monday, July 12, 2010

BASH Colors

I was messing with my .dir_colors file (about.com also has a good article on this) and wanted a chart to help me pick out my colors. So I modified the popular script that is floating around on the internet to display every possible combination in a graphical chart.
The way it is read is foreground and effect is on the row titles and the backgrounds are on the column titles. Hopefully someone else will find this useful.

#!/bin/bash
#
#  This file echoes a bunch of color codes to the 
#  terminal to demonstrate what's available.  Each 
#  line is the color code of one forground color,
#  out of 17 (default + 16 escapes), followed by a 
#  test use of that color on all nine background 
#  colors (default + 8 escapes).
# 
#  Attribute codes: 
#   00=none 01=bold 04=underscore 05=blink 07=reverse 08=concealed
#  Text color codes:
#   30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white
#  Background color codes:
#   40=black 41=red 42=green 43=yellow 44=blue 45=magenta 46=cyan 47=white
#
#  A quick test can be done by echo -e "\033[<color code><test text>\033[0m"
#   e.g. echo -e "\033[01;32mHello World\033[0m"
#
#  Modified from: http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html

T='gYw'   # The test text

# smaller set without attribute codes commented out below
#echo -e "\n                 40m     41m     42m     43m     44m     45m" \
#        "    46m     47m";
#for FGs in '    m' '   1m' '  30m' '  31m' '  32m' '  33m' '  34m' '  35m' \
#           '  36m' '  37m' ;
echo -e "\n                  40m     41m     42m     43m     44m     45m" \
        "    46m     47m";
for FGs in '     m' '    1m' \
           '   30m' '01;30m' '02;30m' '03;30m' '05;30m' '07;30m' '08;30m' \
           '   31m' '01;31m' '02;31m' '03;31m' '05;31m' '07;31m' '08;31m' \
           '   32m' '01;32m' '02;32m' '03;32m' '05;32m' '07;32m' '08;32m' \
           '   33m' '01;33m' '02;33m' '03;33m' '05;33m' '07;33m' '08;33m' \
           '   34m' '01;34m' '02;34m' '03;34m' '05;34m' '07;34m' '08;34m' \
           '   35m' '01;35m' '02;35m' '03;35m' '05;35m' '07;35m' '08;35m' \
           '   36m' '01;36m' '02;36m' '03;36m' '05;36m' '07;36m' '08;36m' \
           '   37m' '01;37m' '02;37m' '03;37m' '05;37m' '07;37m' '08;37m';
  do FG=${FGs// /}  
  echo -en " $FGs \033[$FG  $T  "
  for BG in '40m' '41m' '42m' '43m' '44m' '45m' '46m' '47m';
    do echo -en "$EINS \033[$FG\033[$BG  $T  \033[0m";
  done
  echo;
done
echo

As a side note, for those who may be like myself and didn't see any logic to the codes used for colors, ls and most terminals use the ANSI escape codes (ECMA-48), which is where the numbers come from.

Monday, June 7, 2010

Snapshots of Wave Robot API Now Available

I've Mavenized the Wave Robot API and the Wave model portion of Wave Protocol, a dependency of Wave Robot API and deployed snapshots to my public repository. Currently, I've only deployed snapshots since the use of this artifact is completely untested. I'm working on creating a Maven archetype for Wave robots (which will use this repository until Google uploads it somewhere), and an example using the archetype. After I've done this and am sure it works (and that I haven't done anything stupid with Maven), then I will tag a release and put it on my release repository.

If anyone would like to try it out in the mean time, your feedback would be most appreciated! Here is what you would need in your pom.xml:

<dependencies>
    <dependency>
        <groupId>com.google.wave</groupId>
        <artifactId>wave-robot-api</artifactId>
        <version>20100408-SNAPSHOT</version>
    </dependency>
</dependencies>
<repositories>
    <repository>
        <id>public-repo_maven2-repository</id>
        <url>http://public-repo.googlecode.com/svn/repository/</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
        <releases>
            <enabled>true</enabled>
        </releases>
    </repository>
    <repository>
        <id>public-repo_maven2-snapshots</id>
        <url>http://public-repo.googlecode.com/svn/snapshots/</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
        <releases>
            <enabled>false</enabled>
        </releases>
    </repository>
</repositories>

Some stuff I picked up along the way:
  • When using the WebDAV wagon with Google Code, make sure you use https, otherwise you're liable to get a 405 method not allowed error on a deploy.
  • There's an interesting bug with the Robot API's EventSerializerTest. The behavior is different in Javac 1.5 vs 1.6 but compile fine with the Eclipse compiler. There is bug report for Javac here (fixed in 1.7). I opened a ticket for Google to explicitly cast this in the method so the compiler doesn't have to do any inference, so that way it will build fine with Javac even if they're using JDK 1.6. In the mean time, I've opted to use the Eclipse compiler in the pom rather than altering my local copy of the source. My goal is to make this an exact match to the class files in the zip they've distributed.
  • Guava, the successor to Google Collections (as a backwards compatible superset) and dependency of the Wave model API is on Maven central, but not on Google's repository. It's a little odd, but shouldn't cause any issues.

A special thanks to Jurgen and commenters for the helpful post that got me started.

Friday, June 4, 2010

Bots updated to v2 API

I've updated WorldCat-Bot and Yodaspeakify to v2 of the Wave API, which boasts some fantastic new features, including an easier way to do annotations, doing away with Cron, no need for a separate profile servlet, and the capabilities.xml is automatically generated for you using annotations.

One thing I voted for as an improvement was to use regular expressions in this method. I'm not sure what the author of the issue was talking about with iterating over Elements since, as I understand, these don't include text. The artifacts are still not on Google's repository and I'm getting pretty tired of a 5 minute commit time when I change some jars around.  I'm working on using Google Code as a repository and adding these artifacts after I mavenize them, then I'll create archetypes for Wave -- watch for an update on this soon, now that my bots are done this is the next project.

Some snags I ran into:
The .replace() of BlipContentRefs doesn't work the way you'd think it would. I thought I could do
BlipContentRefs content = blip.first(originalText).replace(newText);
But the BlipContentRefs will be empty. I got a hint for how to do this from wadrobotframework's BlipUtils:
public static void replaceBlipContent(Blip bleep, String nextContent) {
    BlipContentRefs bcr = bleep.all();
    if (bcr != null) {
        bcr.delete();
        bleep.append(nextContent);
    }
}
Thanks, wadael!

Another issue was when I used DocumentChangedEvent and then did a .getBlip() it would return a null Blip. My understanding of the JavaDoc was that it would return the root blip if no Blip was associated with that event, but this didn't seem to be happening. So, I opted for a BlipSubmittedEvent instead. Which, now that I think about it, is probably the desired behavior anyway.

Another issue was when I was trying to take advantage of the cool new filtering options. WorldCat-Bot used to surround it's searches with angle brackets (<>), but when I tried to filter on this
@Capability(filter = "<.*>")
@Override
public void onDocumentChanged(DocumentChangedEvent event) {
  ...
}
It didn't work because the capabilities.xml that was generated was not well formed. I also tried the escaped version
@Capability(filter = "&lt;.*&gt;")
@Override
public void onDocumentChanged(DocumentChangedEvent event) {
  ...
}
But this didn't work either, it looked for the entire literal not the unescaped form. I don't think this is currently possible to do, so I now use square brackets ([]) to filter on.

While trying to debug this, I set up logging (GAE supports JUL), but I learned it only logs things with INFO, WARN, and SEVERE levels (though I couldn't get INFO to work even with the logging.properties file with .level = ALL. Maybe something else is misconfigured).

But overall, the new API has been a very positive experience.

Wednesday, June 2, 2010

GMaven & source encodings

I'm re-posting this because I made some more discoveries after I had initially posted it.

I discovered apparently SourceEncoding is completely broken in GMaven. I tried on Linux where the platform default encoding is UTF-8 and on Windows XP where the platform default encoding is Windows-1252. In both cases, it used ISO-8859-1 as the encoding of the class file it generated, despite the fact the source was encoded in the platform default encoding. I tested this with version 1.2 of the plugin and Groovy 1.7.0. Why this is happening makes no sense to me; I could understand if it used the platform default...but why always ISO-8859-1? I think this is the Jira for this: http://jira.codehaus.org/browse/GMAVEN-13.
I find it shockingly disappointing that a 'critical' issue can go unassigned for almost an entire year. I wonder if it is working in Ant. Their documentation seems to suggest that it does. If that's true, it might tell us something about where the Groovy team's priorities are.

In practice, this often is not problem, since if you're only using ASCII characters in your source code, the first 128 bytes have identical mappings in UTF-8, ISO-8859-1, and Windows-1252, so it won't matter which of those the plugin tries to use. However, I view this as a major obstacle to Groovy's gaining dominance, as it should support Maven (since for better or worst it's become the leading standard) and it should support encodings to support global growth.

Am I missing something? Any good workarounds? I'm also happy to share my experiment project with anyone who wants to play with it.

I've also discovered yet another way in which GMaven's documentation is fail. I was trying to get my project to work with the latest version of Groovy, when I came across this unrelated StackOverflow discussion which use the new 1.2 version of the plugin (which isn't in the archetype or anywhere documented as released -- I thought the artifacts were released by mistake since nothing has even been tagged in their repository for 1.1 or 1.2, although IntelliJ already supports it, plus nabble and some blogs talk about it so I guess it is released). Here are the needed bits from the pom:
<dependencies>
    <dependency>
        <groupId>org.codehaus.gmaven.runtime</groupId>
        <artifactId>gmaven-runtime-1.7</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>org.codehaus.groovy</groupId>
        <artifactId>groovy-all</artifactId>
        <version>1.7.0</version>
    </dependency>
<dependencies>
<plugins>
    <plugin>
        <groupId>org.codehaus.gmaven</groupId>
        <artifactId>gmaven-plugin</artifactId>
        <version>1.2</version>        
        <configuration> 
            <providerSelection>1.7</providerSelection>         
        </configuration>
        <executions>
            <execution>
                <goals>
                    <goal>generateStubs</goal>
                    <goal>compile</goal>
                    <goal>generateTestStubs</goal>
                    <goal>testCompile</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3</version>
        <configuration>
            <source>1.6</source>
            <target>1.6</target>
        </configuration>
    </plugin>
</plugins>

Update: According to this Jira, this is fixed in the 1.3 release. And it appears from my tests that this is the case.

Friday, May 21, 2010

Syntax Highlighting in Blogger: Round 2

So I'm now able to use the popular SyntaxHighlighter that most blogs use. I don't know what was stopping me before: maybe I've gone up in intelligence or they've fixed some bug. There's excellent instructions here. I think I'm going to make the move to this, since it's easier to read, has more features, and is more standard that the Prettify script I was using before. And one additional nice feature is I don't have to manually set my style="overflow:auto;", instead I just do
<pre class="brush:xml; wrap-lines:false">
And there's no longer a need to do an onLoad in the body tag, and a Groovy brush!
I've gone back to old posts and switched the styles, let me know if I've missed any.

Edit: Actually I think maybe the reason was my own lack of attention. I think I forgot to add 'alexgorbatchev.com' to NoScript's whitelist, and that's the host the scripts are currently hosted on, and it magically started working for me because I was in Chrome instead of Firefox. Doh! ... Doh? Who knows for sure?

Edit: The instructions aren't really up to date anymore, as new brushes have been added. Here is an updated example (note this uses the default theme, but you can choose to use other themes):
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shCore.css" rel="stylesheet" type="text/css"></link>
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" rel="stylesheet" type="text/css"></link>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript">
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushAS3.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushBash.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushColdFusion.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCpp.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCSharp.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCss.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushDelphi.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushDiff.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushErlang.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushGroovy.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJava.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJavaFX.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJScript.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPerl.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPhp.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPlain.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPowerShell.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPython.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushRuby.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushScala.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushSql.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushVb.js' type='text/javascript'/>
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js' type='text/javascript'/>
<script language='javascript'>
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.config.clipboardSwf = 'http://alexgorbatchev.com/pub/sh/current/scripts/clipboard.swf';
SyntaxHighlighter.all();
</script>
This still goes inside the header area of the template, which means you'll still need to manually edit the HTML.

Monday, January 11, 2010

More Groovy Bugs

While trying to add some sorting to my ValidateMe script, I discovered a Groovy bug that someone else reported on Thursday. It appears that although inner classes are supposed to be supported in the new Groovy 1.7, calling methods on enums inside a class do not. I've also created another suggestion for a reverse() method on Maps. I did add the changes I wanted to ValidateMe without these Jiras being addressed. What I did for the reverse was do something like
theMap.reverse{it.value}

Wednesday, December 16, 2009

Launching a GroovyConsole Without a cmd window

I run Groovy 1.7 from the .zip files (which doesn't yet have the native launchers built for it), and I love the line numbering, and many other things about it. The one thing that was irritating is that a new cmd window would have to be opened every time I launched the groovyConsole.bat. I now have a workaround. Create a new .vbs file in the bin folder of your groovy with the following contents:
Set WshShell = WScript.CreateObject("WScript.Shell")
obj = WshShell.Run("groovyconsole.bat", 0)
set WshShell = Nothing
You can then put a shortcut to this wherever, and even make it pretty by setting the icon to this. Oh, and make sure you have your GROOVY_HOME set up.

Actually, this can be used to run any batch script in the background, as long as you don't need to be able to let the user pass in arguments. My thanks to Koushik Biswas from Yahoo Answers for the tip.

Friday, December 11, 2009

Which Child Am I?

I don't know if this is worth blogging home about, but I've needed this solution a couple times, and I found myself referencing this blog entry draft, so I'll put it out there and maybe someone else will find it useful. The problem is I have an xml node, and I want to know the order it is (as an index integer) in relation to its siblings. You may have to modify this of course, if the children you are comparing are deeper down than 1.

def CAR_RECORDS = '''
<records>
  <car name='HSV Maloo' make='Holden' year='2006'>
    <country>Australia</country>
    <record type='speed'>Production Pickup Truck with speed of 271kph</record>
  </car>
  <car name='P50' make='Peel' year='1962'>
    <country>Isle of Man</country>
    <record type='size'>Smallest Street-Legal Car at 99cm wide and 59 kg in weight</record>
  </car>
  <car name='Royale' make='Bugatti' year='1931'>
    <country>France</country>
    <record type='price'>Most Valuable Car at $15 million</record>
  </car>
</records>'''

def records = new XmlSlurper().parseText(CAR_RECORDS)
def record = records.car[2].country

int index = 0
Boolean found = false
record.parent().parent().children().each {
  if (it == record.parent()) {
    found = true
  } else if (!found) {
    index++
  }
}
assert index == 3
return index

Wednesday, November 11, 2009

Groovy - Sometimes you still need a semicolon

I Was ploughing through my overly-large blogroll the other night when this article caught my eye.
http://groovy.dzone.com/articles/groovy-sometimes-you-still
I gave it a read, because I wanted to know all the cases where you had to have a semicolon.

He gives two examples, the first of which he’s wrong about, it works fine in 1.6.5 and I believe it works for anything >= 1.6.
def list = [1,2,3] as List<Integer>
println list

This second one (not the exact same example he used) does need a semicolon
{-> assert true == true }()
{-> assert false == false }()

Should be
{-> assert true == true }();
{-> assert false == false }()

This is only the case if you have two closure calls next to each other with nothing in between.
{-> assert true == true }()
println ""
{-> assert false == false }()
works fine.

Are there other times when you need a semicolon at the end of the line (assuming only one statement per line)?

Friday, November 6, 2009

Groovy 1.7 Beta 2 Available

It’s a bit of old news, but I don’t remember seeing anything about it until today.
It was available 12 Oct, and Windows installer binaries were available 18 Oct. Get yourshere.

A draft of the current features is available at http://docs.codehaus.org/display/GROOVY/(draft)+Groovy+1.7+release.  This release is mostly bug fixes, with two notable improvements.  They have added the ability to alter the meaning of Groovy Truth.  This lets you add some truth to your own class by adding the asBoolean method:

class Foo {
  String value
  boolean asBoolean() { value == "something true" }
}
assert new Foo(value: "something true")
assert !new Foo(value: "teh cake is a lie")

And of course, using ExpandoMetaClass, you can alter the behavior for Groovy classes that already have a Groovy Truth defined:
Integer.metaClass.asBoolean { int value ->
  return value > 0
}
Integer foo = 7
assert foo.asBoolean()

They’ve also added stylish outputs for assertion failures (I guess taken from the Spock testing framework):
int foo = 0
int bar = 1
assert foo == bar
will display:
Assertion failed:
assert foo == bar
       |   |  |
       0   |  1
         false

Friday, October 16, 2009

Java vs Groovy: Polymorphism

While doing some studying for SCJP, I was tinkering with some stuff in the Groovy console as a way of testing some stuff in Java and I found they actually behave differently.

class Person {
  protected String name
  protected int age
  
  public Person() {
    name = "secret"
    age = -1
  }
}

class John extends Person {
  int favoriteNumber
  
  public John() {
    name = "nobody special"
    age = 0
    favoriteNumber = 7
  }
  
  public String doNothing() {
    return "junk"
  }
}

Person p = new John()
println p.doNothing()

If you do this in Java (splitting the classes out to their own files of course), it won't compile. Java looks at the reference type for available methods, so you will get a NoSuchMethodException. In Groovy, however, it looks at the type of the object, not the type of the reference so the method is found at runtime. And this is probably what you would want, so I can refer to John or Mary as a generic Person, but they still do the things they do in a John or Mary way.

Java vs Groovy: Overriding static methods

The Java equivalent of below will not compile, but in Groovy it works fine. Groovy supports overriding static methods, whereas Java does not.

class Person {
  protected String name
  protected int age
  
  public Person() {
    name = "secret"
    age = -1
  }
  
  public static String doNothing() {
    return "junk"
  }
}

class John extends Person {
  int favoriteNumber
  
  public John() {
    name = "nobody special"
    age = 0
    favoriteNumber = 7
  }
  
  public String doNothing() {
    return "more junk"
  }
}

Person p = new John()
println p.doNothing()

I don't think this would be a problem. You might want each inherited class to have its own (maybe static) version of a method, but for every instance of the class to only have one in memory. If you don't want the children overriding the method, you should just make them final.

Thursday, October 15, 2009

Code in Blogger

So, I've been looking for a way to make my code examples in blog posts more readable. I had tried to use the hosted version of SyntaxHighlighter, but couldn't seem to get it working right (I'm sure it was something simple) and it needed several lines added to the Blogger html template. Instead, I've opted to use google-code-prettify. Though it doesn't support as many languages (in particular no groovy), all I needed was 2 lines pasted into the template, and making it load the class when the body element is. Couldn't be simpler. I followed the instructions (slightly tweaked) from here. Thanks Luka.

These are the lines you need (anywhere in head tag):
<link href="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js" />
Then modify body tag to load the script:
...
<body onload='prettyPrint()'>
...
Use:
<pre class="prettyprint" style="overflow:auto;">
  <!-- Your code goes here -->
</pre>
You don't need to specify the language since the script will guess, but you can if you like:
<pre class="prettyprint lang-html">
  <!-- HTML code here -->
</pre>
The lang-* class specifies the language file extensions. File extensions supported by default include "bsh", "c", "cc", "cpp", "cs", "csh", "cyc", "cv", "htm", "html", "java", "js", "m", "mxml", "perl", "pl", "pm", "py", "rb", "sh", "xhtml", "xml", "xsl".

I've gone back and added the syntax highlighting to all my code examples in previous posts.