Author: oldoldo
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:
- The <% @include %> directive for static file inclusion
- <jsp:include> or <c:import> tags for runtime inclusion
- 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} <${p.email}>">
<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.
The official Java tutorials
Sometimes people ask me how to learn Java, or aspects of Java. As a quick and free option, I usually recommend the Official Java tutorials.
Of course, you should do this hands-on, after installing a recent JDK and a decent text editor or IDE.
Direct Google image search URLs
Problem: This URL does not actually execute an image search:
http://images.google.com/?q=oliver+doepner
For direct links to a Google image search, I have successfully used this URL pattern:
http://google.com/search?tbm=isch&q=oliver+doepner
The “+” sign is just a URL encoded space character.
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
- Use NLS_CHARACTERSET = AL32UTF8 and regular VARCHAR2 columns
- 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" %>
Java coding conventions and guidelines
My favorite sources for Java coding conventions and guidelines:
- Code Conventions for the Java Programming Language (Sun, now Oracle)
- Java Coding Standard (NASA / Jet Propulsion Laboratory, Caltech)
- Secure Coding Standard for Java (CERT / Oracle)
- Book: Effective Java, 2nd ed. (Josh Bloch, Sun / now Google)
- Book: Clean Code (Robert C. Martin aka “Uncle Bob”)
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>.
Install latest JRuby/Rails on Debian
The Debian stable distribution (“squeeze”) includes JRuby 1.5.1, which is old.
This is how I installed the latest JRuby:
sudo apt-get install curl curl -L https://get.rvm.io | bash -s stable rvm install jruby rvm use jruby jruby —S gem install rails