Minimal pom.xml for executable jar

When you develop a stand-alone Java application, Maven can create the executable jar for you, with main-class and classpath manifest entries. You just need the configuration of the maven-jar-plugin shown below.

The sample pom.xml also specifies that we use Java 7 and UTF-8. You can take out the sourceEncoding and maven-compiler-plugin configurations, if you want to go with defaults instead.

<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>net.doepner</groupId>
    <artifactId>executable-jar-sample</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.0</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <mainClass>net.doepner.sample.Main</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Here is a matching minimal main class, src/main/java/net/doepner/sample/Main.java:

package net.doepner.sample;

import javax.swing.JOptionPane;

/**
 * The class that has the main method
 */
public class Main {

    public static void main(String[] args) {
        JOptionPane.showMessageDialog(null, "It works!");
    }
}

JSP tag files – Simple typesafe markup reuse

Disclaimer: This is old information. But I still often talk to Java or JSP developers who were not aware of this feature, even though it was introduced in JSP 2.x, many years ago.

Another disclaimer: When I say “typesafe” I mean that tag files support the declaration of parameter types and some advanced IDEs like Intellij will actually use that information at source edit time to help the developer avoid runtime errors. If you want to get more reliable type safety independent of the IDE, you might want to consider a JSP pre-compilation approach like this one.

Ok, so now to the actual thing I wanted to write about:

If you have repetitive markup in your JSP pages you have several options for reuse, including:

  1. The <% @include %> directive for static file inclusion
  2. <jsp:include> or <c:import> tags for runtime inclusion
  3. Complicated “classic” JSP custom tags with tag handler Java code

All of these have limitations or problems: Static includes do not support any parametrization, jsp:include and c:import tags only support String parameters (via nested jsp:param tags) and custom tags with tag handlers and tab library descriptors require writing and compiling Java code that generates markup which is a bad practice and complicated.

JSP tag files, introduced in JSP 2.0 (JEE 1.4) solve all these problems and allow simple, straightforward and compact markup reuse that supports parameters of any Java type. The reference documentation is in the JEE 1.4 tutorial.

For example, put these lines into WEB-INF/tags/email.tag:

<%@ attribute name="p" required="true" type="net.doepner.Person" %>

<span title="Send email to ${p.name} &lt;${p.email}&gt;">
    <a href="mailto:${p.email}">${p.name}</a>
</span>

Then you can use it in any of your JSP pages as a tag like this, assuming the page has access to a “company” bean that has a getBoss() method:

<%@ taglib prefix="x" tagdir="/WEB-INF/tags" %>

<x:email p="${company.boss}" />

The good part about declaring the attribute type in your tag file is that IDEs like IntelliJ can provide all the nice things like code completion, refactoring support, javadoc display, etc for the EL expressions, like p.email or p.name in the example.

If the Java type of the “company” bean is known to the IDE, then it will even show an error in case the return type of getBoss() was not a subtype of Person.

Other nice things about the tag file approach:

  • No tag library descriptor (tld) file required
  • Tag attributes have local scope (not polluting the session, request or page scope)

Packaged tag files

A very cool thing about tag files is that they can be packaged in jars and reused in other web applications. This page in the J2EE 1.4 tutorial explains the details.

HTML / JSP / Servlets / JavaMail / Oracle / CSV / Excel: UTF-8 to Unicode them all

Recently I wrote a web app that

  • Lets user enter a greeting message with subject and body
  • Sends an HTML email (“ecard”) to recipients
  • Stores info about sent messages in Oracle
  • Reports on recently sent messages on an admin page (HTML table)
  • Provides the report as downloadable CSV files (often opened in M$ Excel)
  • Provides an RSS feed about recently sent messages

One goal was to allow any Unicode characters for subject and body text and make sure that web form, servlets, JSP pages, emails, database records and CSV files all support that (no garbled characters anywhere, no data loss through charset conversions).

So here is what I did:

JSP and HTML pages

At the top of the JSP pages:

<!DOCTYPE html>

<%@ page contentType="text/html;charset=UTF-8" %>

In every HTML and JSP page, within the <head> section:

    <meta charset="UTF-8"/>

Servlet filter

In WEB-INF/web.xml:

    <filter>
        <filter-name>UTF8Filter</filter-name>
        <filter-class>net.doepner.servlet.Utf8Filter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>UTF8Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

In net/doepner/servlet/Utf8Filter.java:

package net.doepner.servlet;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

/**
 * Makes sure that we use UTF-8 for all requests and response
 */
public class Utf8Filter implements Filter {

    @Override
    public void init(FilterConfig fc) throws ServletException {
        // nothing to do
    }

    @Override
    public final void doFilter(ServletRequest request,
                               ServletResponse response,
                               FilterChain chain)
            throws IOException, ServletException {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // nothing to do
    }
}

Sending Email

In the code that sends the email (using javax.mail API):

        final MimeBodyPart htmlPart = new MimeBodyPart();
        htmlPart.setContent(template.getHtml(msg), "text/html;charset=utf-8");

        final Multipart multiPart = new MimeMultipart("alternative");
        multiPart.addBodyPart(htmlPart);

        final MimeMessage email =
                new MimeMessage(Session.getDefaultInstance(properties));

        // setting the sender and recipient is omitted here for brevity

        email.setSubject(msg.getSubject(), "UTF-8");
        email.setContent(multiPart);

        Transport.send(email);

Oracle database

For Unicode support in Oracle, make sure that

  1. Use NLS_CHARACTERSET = AL32UTF8 and regular VARCHAR2 columns
  2. Or use NVARCHAR2 column types.

I used approach A. I haven’t actually tried approach B myself.

Here is a useful query to see current charset settings:

SELECT * FROM nls_database_parameters nls 
         WHERE nls.parameter LIKE '%CHAR%SET%';

CSV generation

See my earlier blog post about CSV generation in a Servlet using my CsvWriter utility class.

The important bits are:

private static final char BYTE_ORDER_MARK = (char) 0xfeff;

Put that byte sequence (the so-called “BOM“) at the very beginning of the response content. Some applications (like M$ Excel) will otherwise not detect the UTF-8 encoding correctly.

Do this on the writer object from the getWriter() method on the servlet response:

// The BOM is required so that Excel will recognize UTF-8
// characters properly, i.e. all non-ASCII letters, etc.
writer.print(BYTE_ORDER_MARK);

RSS feed

I generate the RSS feed with an JSP page. Just make sure you have this on the top of the page:

<?xml version="1.0" encoding="UTF-8"?>
<%@ page contentType="text/xml;charset=UTF-8" %>

Easily generate CSV in Java (e.g. from Servlet)

To generate CSV from Java consider this simple interface:

package net.doepner;

import java.io.IOException;

/**
 * Generates CSV (comma separated values) for rows of Java objects
 */
public interface ICsvWriter {

    /**
     * Adds a row of objects to the CSV document
     *
     * @param values The objects in the row (count must match the number of the
     *               headers)
     */
    void row(Object... values);

    /**
     * Writes CSV based on the the String representations of the objects
     *
     * @param appendable The writer to append to
     * @throws IOException If underlying IO fails
     */
    void appendTo(Appendable appendable) throws IOException;
}

I implemented the interface with this CsvWriter class:

package net.doepner;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.regex.Pattern;

/**
 * Convenient generation of CSV
 */
public class CsvWriter implements ICsvWriter {

    private static final CharSequence CSV_ROW_END = "\r\n";
    private static final char LINE_BREAK_WITHIN_CELL = '\n';

    private static final Pattern QUOTES = Pattern.compile("\"");
    private static final String ESCAPED_QUOTE = "\"\"";

    private final Object[] headers;
    private final Collection<Object[]> rows = new LinkedList<Object[]>();

    /**
     * @param headers The objects representing the column headers
     */
    public CsvWriter(Object... headers) {
        this.headers = Arrays.copyOf(headers, headers.length);
        if (cols() == 0) {
            throw new IllegalArgumentException("No columns");
        }
    }

    @Override
    public final void row(Object... values) {
        if (values.length != cols()) {
            throw new IllegalArgumentException("Specify " + cols() + "values ");
        }
        rows.add(Arrays.copyOf(values, values.length));
    }

    @Override
    public final void appendTo(Appendable appendable) throws IOException {
        appendRow(appendable, headers);

        for (Object[] row : rows) {
            appendRow(appendable, row);
        }
    }

    private static void appendRow(Appendable appendable, Object[] row)
            throws IOException {
        boolean first = true;
        for (Object value : row) {
            if (first) {
                first = false;
            } else {
                appendable.append(",");
            }
            appendable.append('"');
            appendable.append(toCsvString(value));
            appendable.append('"');
        }
        appendable.append(CSV_ROW_END);
    }

    private static CharSequence toCsvString(Object object) {
        if (object instanceof Iterable) {
            final StringBuilder sb = new StringBuilder();
            boolean first = true;
            for (Object o : (Iterable<?>) object) {
                if (first) {
                    first = false;
                } else {
                    sb.append(LINE_BREAK_WITHIN_CELL);
                }
                sb.append(toCsvString(o));
            }
            return sb.toString();
        } else {
            if (object == null) {
                return "";
            } else {
                final String s = object.toString();
                return QUOTES.matcher(s).replaceAll(ESCAPED_QUOTE);
            }
        }
    }

    private int cols() {
        return headers.length;
    }
}

Example for how it can be used in a Servlet, here with full support for Unicode characters using UTF-8, in a way that even Excel understands:

    private static final char BYTE_ORDER_MARK = (char) 0xfeff;
 
    private static void generateCsv(ServletResponse resp, 
                                    Iterable<IMessage> messages)
            throws ServletException {

        resp.setContentType("text/csv");
        resp.setCharacterEncoding("UTF-8");

        final ICsvWriter csv = new CsvWriter(
                "Date", "Sender", "Recipients", "Subject");

        for (IMessage msg : messages) {
            csv.row(msg.getDateTime(), msg.getSender(), 
                    msg.getRecipients, msg.getSubject());
        }

        final PrintWriter writer = getResponseWriter(resp);
        try {
            // The BOM is required so that Excel will recognize UTF-8
            // characters properly, i.e. all non-ASCII letters, etc.
            writer.print(BYTE_ORDER_MARK);
            writer.flush();

            csv.appendTo(writer);
            writer.flush();

        } catch (IOException e) {
            throw new ServletException(e);
        }
    }

    private static PrintWriter getResponseWriter(ServletResponse resp)
            throws ServletException {
        try {
            return resp.getWriter();
        } catch (IOException e) {
            throw new ServletException(e);
        }
    }

Tomcat: Require authentication but no roles

Sometimes you might want to protect some parts of your Java web application from anonymous access, but not impose any authorization constraints: Every authenticated user should be automatically authorized, no matter what roles they have or don’t have.

Tomcat supports this by setting allRolesMode=”authOnly” on the Realm definition, usually in META-INF/context.xml, in combination with <security-constraint> entries in WEB-INF/web.xml that declare an <auth-constraint> with <role-name>*</role-name>.

JRuby on Rails 3.2

I currently have to work on an application using JRuby on Rails 3.2. My background is 12+ years of Java centric web development using Eclipse or IntelliJ.

There are a few things that I love about working with Java in Eclipse or IntelliJ that I miss when working with Rails:

  • Java’s backwards compatibility
  • Java’s cross-platform consistency
  • That Java has a static type system and is a compiled language
  • The relatively rigid syntax of Java that prevents idiosyncratic DSLs
  • Being able to control mutability, extensibility and visibility at compile-time (private, final)
  • Java’s distinction of interfaces vs implementation classes
  • Powerful auto-completion and Refactoring in the IDE
  • Powerful code inspections and quickfixes in the IDE
  • IDE and compiler feedback about mistakes and design flaws in my code
  • The powerful and rich ORM features of JPA and Hibernate

Dynamic language fans might cringe at some of the points listed above. Feel free to provide constructive feedback or well-reasoned criticism.

There are however a couple of things that I like about Ruby on Rails 3.2:

  • Dependency management using gems and bundler
  • A very standardized project layout (directory structure and conventions)
  • Code generation (as a convenient way to quickly create sample code)
  • Out-of the box integration and support for JQuery, CoffeeScript, SASS
  • Cool gems like colorbox-rails for turning a regular view into a modal dialog
  • The asset pipeline

I am still in the early phase of my Rails experience and I will try to post more about it as things evolve.

Project Jigsaw notes and links

Goals
– Help fix problems with the classpath
– Allow modularization of JDK/JRE
– Enable using JSE subsets
– Improve performance (startup time, download time, etc.)

OSGi was considered too complex and not well-suited for JDK modularization
But: Project Penrose tries to make sure that OSGi can be implemented on top of Jigsaw.

Project is in Phase 1 (exploration, prototyping)
Phase 2 (reference implementation) in very early stages

Project website
Big picture of the Design

A lot of work to do
Was recently deferred from Java 8 to Java 9

Challenges:
– Dynamic modularization
– JEE containers
– Needs a reflective API
– Fundamental changes, require a lot of QA (testing, etc.)

But Java 8 might already introduce a simplified way of using JSE subset:
“JSE Profiles”

Module descriptor: module-info.java
http://openjdk.java.net/projects/jigsaw/doc/lang-vm.html#jigsaw-1.1

Modules are versioned
Only one version of a module can exist

Repositories: HTTP server or local files

Packager to generate deb, rpm packages etc. from Java modules
translate the module metadata to package metadata

JDK modularization
http://openjdk.java.net/projects/jigsaw/doc/jdk-modularization.html

Backwards compat:
Classpath-mode (legacy), Module mode

But: No more dependecies on internal classes (e.g. sun.misc.*) will be possible
No more rt.jar, tools.jar

A font chooser combobox for Swing or AWT components

Useful if you want to let the user choose the font to use for certain components of a Swing UI:

package net.doepner.ui.text;

import java.awt.Component;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Arrays;
import java.util.Comparator;

import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JList;
import javax.swing.ListCellRenderer;

public class FontChooser extends JComboBox<Font> {

	public FontChooser(final Component... components) {

		final Font[] fonts = GraphicsEnvironment
				.getLocalGraphicsEnvironment()
				.getAllFonts();

		Arrays.sort(fonts, new Comparator<Font>() {
			@Override
			public int compare(Font f1, Font f2) {
				return f1.getName().compareTo(f2.getName());
			}
		});

		for (Font font : fonts) {
			if (font.canDisplayUpTo(font.getName()) == -1) {
				addItem(font);
			}
		}

		addItemListener(new ItemListener() {
			@Override
			public void itemStateChanged(ItemEvent e) {
				final Font font = (Font) e.getItem();
				for (Component comp : components) {
					setFontPreserveSize(comp, font);
				}
			}
		});
		
		setRenderer(new FontCellRenderer());
	}
	
	private static class FontCellRenderer 
			implements ListCellRenderer<Font> {
		
		protected DefaultListCellRenderer renderer = 
				new DefaultListCellRenderer();
		
		public Component getListCellRendererComponent(
				JList<? extends Font> list, Font font, int index, 
				boolean isSelected, boolean cellHasFocus) {
			
			final Component result = renderer.getListCellRendererComponent(
					list, font.getName(), index, isSelected, cellHasFocus);
			
			setFontPreserveSize(result, font);
			return result;
		}
	}

	private static void setFontPreserveSize(final Component comp, Font font) {
		final float size = comp.getFont().getSize();
		comp.setFont(font.deriveFont(size));
	}
}