Doing RPC with REST
RPC doesn't really rhyme with REST. REST revolves around the concept of resources, and the basic methods offered by the HTTP protocol (being GET, PUT, POST, DELETE and some others). Now, sometimes you're confronted with something outside the scope of those methods. Say, for example, you want to be able to send a client's data to the ERP system by simply doing 1 REST call.
Option 1 is to create an URL template like this: http://host/client/{id}/sendToERP. But that isn't REST. sendToERP isn't a resource, it's an action. So that one's gone.
Option 2 is to create an URL template like this: http://host/client/{id}?action=sendToERP. Better, but to which HTTP method will you bind this. Is it a GET? A PUT? Not really sure. You're not really altering the client, but you're not asking for the data either...
So that brings us to option 3: the command pattern. Assume we have a URL template like this: http://host/client/{id}/command. Doing a get on this will return all the possible commands for that client. In our case this might be:
<commands>
<command id="1">
<type>sendToERP</type>
</command>
<command id="2">
<type>updateFromERP</type>
</command>
</commands>
So now we can do this to execute the sendToERP command: GET http://host/client/{id}/command/1. What does this return? Well, it can for example return the result of the command, like this:
<commandResult>
<result>
OK
</result>
</commandResult>
Code snippet: replacing java.util.Logger by SLF4J
This simple piece of code adapts all JUL statement to be forwarded to SLF4J:
java.util.logging.Logger rootLogger = LogManager.getLogManager().getLogger("");
Handler[] handlers = rootLogger.getHandlers();
for (Handler handler : handlers) {
rootLogger.removeHandler(handler);
}
SLF4JBridgeHandler.install();
The first 5 lines are needed because otherwise JUL will still log to System.err.
Creating a custom binder for Spring RCP
Introduction
For those not familiar with RCP, a quick heads-up. Spring RCP is a Swing application framework, based on the Spring Framework. It utilizes many of Spring's available utilities, the Spring IOC container being the most important one.
Bindings and binders
Spring RCP gives you the possibility to create controls that are bound to certain properties of a given object. These bound controls are called bindings. A binder is a class that creates bindings. A binding factory, if you will.
Standard Spring RCP has some basic binders, but often you will encounter user requirements that require you to build a custom binder. For example, a binder to show a list of objects in a JTable, a binder to show a String in a formatted text field or even a binding to show and select an image. These are not standard in RCP, and you'll need to make those yourself.
Adding security to CXF web services
In my previous post, I demonstrated how easy it is to create REST services using Spring and CXF. Now I'll show you something even easier: adding security.
For this example, I'll start where I left off, which is a working REST service showing the time, and add basic HTTP authentication. I'll be using Spring Security 3 to add the security features. Spring Security 3 is relatively new and those used to version 2 will need a wee bit of adjustment (you'll need more jars, as they split them up).
Creating a REST service with CXF and Spring in 10 minutes
I love REST. It's easy, straightforward and above all: easily testable. When you're developing with REST, you have a lot of options to choose from. There's Jersey, the reference Sun implementation, then you have RestEasy, the JBoss choice, and there is CXF, the Apache choice. I chose CXF for this example simply because it's so easy to create REST services with it, without a lot of configuration.
Propagating security through Spring’s HttpInvoker service export
I've been looking into securing my HttpInvoker exported services lately, and I feel that the current Spring reference falls a bit short when it comes to integrating security into HttpInvoker. They'll point you to AuthenticationSimpleHttpInvokerRequestExecutor, which adds basic authentication to your HttpInvoker calls. You'll then have to use the DelegatingFilterProxy to secure your services on the server side.
But what if you already have your SecurityContext on the client side, say for example in a Spring Rich Client application? It would cause a lot of overhead, as each call will retrieve the security context for the user (which you already have). In comes ContextPropagatingRemoteInvocationFactory. This class is normally used in the RMI exporter context, but suits my use case rather nicely. It put the current security context on the client in the invocation object, and when the invocation is executed on the server, it first sets the current security context (which will normally be thread local) to the enclosed security context.
As HttpInvoker is a Spring specific approach to service remoting, I don't really see any problem with this approach. It's not like someone is going to call a HttpInvoker service by hand (it can be done), and even then they still have to be able to correctly set the security context inside the call. In short, if your web API can be called outside of a Spring context or through Spring without a known SecurityContext (but with a username and password), use DelegatingFilterProxy to secure your remote services. Otherwise use the ContextPropagatingRemoteInvocationFactory. It's so much easier to set up.
Excluding a dependency throughout the POM a.k.a. “the version 99 fix”
Although I am a Maven fanatic, that doesn't mean I don't get mad because of some missing features in Maven. One of those is general exclusion of dependencies.
Most projects still use either log4j or commons-logging for their logging needs. While there is nothing wrong with that, I prefer SLF4J. SLF4J also has these really nice adapters for libraries that use those other logging frameworks so everything gets logged through it anyway. And here lies the problem. When you want to use these adapter classes, you need to exclude the real log4j and commons logging libraries. When you only have like 3 dependencies, that's not a big issue. If you have 50+, it is. So you're left with 2 choices:
- Exclude log4j and commons logging from every dependency (you'll need to look at the POM to see which one it uses)
- Use the somewhat ugly, but effective version 99-does-not-exist fix
Having fun with JSON and DbUnit
DbUnit is one of those invaluable frameworks in my toolbox. The only thing I don't like are the XML dataset formats. Having played around with JSON for the last couple of weeks, I decided a nice exercise would be to create a JSON-based dataset.
I started off with the dataset format I'd like to use
{
"mytable":
[
{
"field1": "value1",
"field2": "value2"
},
{
"field1": "value3",
}
]
}
This would create 2 rows in the mytable database table, with the second row having its field2 set to NULL. Writing the code for DbUnit to be able to process this took me about 15 minutes (including unit tests for it). It uses Jackson for the JSON processing. Here it is:
import org.codehaus.jackson.map.ObjectMapper;
import org.dbunit.dataset.*;
import org.dbunit.dataset.datatype.DataType;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
* DBUnit DataSet format for JSON based datasets. It is similar to the flat XML layout,
* but has some improvements (columns are calculated by parsing the entire dataset, not just
* the first row). It uses Jackson, a fast JSON processor.
* <br/><br/>
* The format looks like this:
* <br/>
* <pre>
* {
* "<table_name>": [
* {
* "<column>":<value>,
* ...
* },
* ...
* ],
* ...
* }
* </pre>
* <br/>
* I.e.:
* <br/>
* <pre>
* {
* "test_table": [
* {
* "id":1,
* "code":"JSON dataset",
* },
* {
* "id":2,
* "code":"Another row",
* }
* ],
* "another_table": [
* {
* "id":1,
* "description":"Foo",
* },
* {
* "id":2,
* "description":"Bar",
* }
* ],
* ...
* }
* </pre>
*
* @author Lieven DOCLO
*/
public class JSONDataSet extends AbstractDataSet {
// The parser for the dataset JSON file
private JSONITableParser tableParser = new JSONITableParser();
// The tables after parsing
private List<ITable> tables;
/**
* Creates a JSON dataset based on a file
* @param file A JSON dataset file
*/
public JSONDataSet(File file) {
tables = tableParser.getTables(file);
}
/**
* Creates a JSON dataset based on an inputstream
* @param is An inputstream pointing to a JSON dataset
*/
public JSONDataSet(InputStream is) {
tables = tableParser.getTables(is);
}
@Override
protected ITableIterator createIterator(boolean reverse) throws DataSetException {
return new DefaultTableIterator(tables.toArray(new ITable[tables.size()]));
}
private class JSONITableParser {
private ObjectMapper mapper = new ObjectMapper();
/**
* Parses a JSON dataset file and returns the list of DBUnit tables contained in
* that file
* @param jsonFile A JSON dataset file
* @return A list of DBUnit tables
*/
public List<ITable> getTables(File jsonFile) {
try {
return getTables(new FileInputStream(jsonFile));
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* Parses a JSON dataset input stream and returns the list of DBUnit tables contained in
* that input stream
* @param jsonStream A JSON dataset input stream
* @return A list of DBUnit tables
*/
@SuppressWarnings("unchecked")
public List<ITable> getTables(InputStream jsonStream) {
List<ITable> tables = new ArrayList<ITable>();
try {
// get the base object tree from the JSON stream
Map<String, Object> dataset = mapper.readValue(jsonStream, Map.class);
// iterate over the tables in the object tree
for (Map.Entry<String, Object> entry : dataset.entrySet()) {
// get the rows for the table
List<Map<String, Object>> rows = (List<Map<String, Object>>) entry.getValue();
ITableMetaData meta = getMetaData(entry.getKey(), rows);
// create a table based on the metadata
DefaultTable table = new DefaultTable(meta);
int rowIndex = 0;
// iterate through the rows and fill the table
for (Map<String, Object> row : rows) {
fillRow(table, row, rowIndex++);
}
// add the table to the list of DBUnit tables
tables.add(table);
}
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
return tables;
}
/**
* Gets the table meta data based on the rows for a table
* @param tableName The name of the table
* @param rows The rows of the table
* @return The table metadata for the table
*/
private ITableMetaData getMetaData(String tableName, List<Map<String, Object>> rows) {
Set<String> columns = new LinkedHashSet<String>();
// iterate through the dataset and add the column names to a set
for (Map<String, Object> row : rows) {
for (Map.Entry<String, Object> column : row.entrySet()) {
columns.add(column.getKey());
}
}
List<Column> list = new ArrayList<Column>(columns.size());
// create a list of DBUnit columns based on the column name set
for (String s : columns) {
list.add(new Column(s, DataType.UNKNOWN));
}
return new DefaultTableMetaData(tableName, list.toArray(new Column[list.size()]));
}
/**
* Fill a table row
* @param table The table to be filled
* @param row A map containing the column values
* @param rowIndex The index of the row to te filled
*/
private void fillRow(DefaultTable table, Map<String, Object> row, int rowIndex) {
try {
table.addRow();
// set the column values for the current row
for (Map.Entry<String, Object> column : row.entrySet()) {
table.setValue(rowIndex, column.getKey(), column.getValue());
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
}
I tested it with 1000+ row dataset and it is quite fast. It's even documented! Have fun.
Self-signed JAR signing with Maven for webstart – without the keystore
Anyone reading the above title will have serious doubts about the validity of this article. But let me explain. Mind you, this is an article for development purposes, for production you'll probably need to have a keystore with a properly signed SSL certificate (then again, most webstart applications are intranet, so why bother).
That the Maven Webstart plugin hosted by Codehaus is ill-documented is a well known fact. However, as with most Maven-related subjects, you can learn a whole lot about its workings by looking at the code, or the examples if they are provided.
A beginner will read the plugin documentation, and then start fidgeting with the Java keytool utility to get the keystore needed for jar signing. Needless to say, the keytool utility isn't the easiest one of the lot. Probably after about half an hour you'll have a decent enough keystore, going through about every function of the keytool command-line. Then he'll utilize the keystore in the plugin (preferably using a relative path).
Why the Codehaus guys haven't mentioned the easier way on the plugin's website is beyond me, but here it is:
<plugin>
<groupId>org.codehaus.mojo.webstart</groupId>
<artifactId>webstart-maven-plugin</artifactId>
<executions>
<execution>
<phase>process-resources</phase>
<goals>
<goal>jnlp-download-servlet</goal>
</goals>
</execution>
</executions>
<configuration>
<outputDirectoryName>webstart</outputDirectoryName>
<excludeTransitive>false</excludeTransitive>
<jnlpFiles>
<jnlpFile>
<templateFilename>template.vm</templateFilename>
<outputFilename>myapp.jnlp</outputFilename>
<jarResources>
<jarResource>
<groupId>be.insaneprogramming</groupId>
<artifactId>myapp-gui</artifactId>
<version>${project.version}</version>
<mainClass>be.insaneprogramming.myapp.GUIMain</mainClass>
</jarResource>
</jarResources>
</jnlpFile>
</jnlpFiles>
<sign>
<keystore>/tmp/myappkeystore</keystore>
<keypass>m2m2m2</keypass>
<storepass>m2m2m2</storepass>
<alias>foobar</alias>
<validity>3650</validity>
<dnameCn>Insane Programming</dnameCn>
<dnameOu>Software Development</dnameOu>
<dnameO>Insane Programming</dnameO>
<dnameL>Maldegem</dnameL>
<dnameSt>Oost-Vlaanderen</dnameSt>
<dnameC>BE</dnameC>
<verify>false</verify>
<keystoreConfig>
<delete>true</delete>
<gen>true</gen>
</keystoreConfig>
</sign>
<outputJarVersions>true</outputJarVersions>
<verbose>true</verbose>
<unsign>true</unsign>
<verifyjar>false</verifyjar>
</configuration>
</plugin>
This configuration will create a keystore on the fly and use that one for the signing. The advantage?
- No keystore is needed in the project sourcetree or in a shared location
- No more messing around with the keytool
- Signing information is kept in the POM
- You can reuse information in the POM for the signing (organisation name etc.), or even use Maven properties and profiles to customize the signing without having to resort to multiple keystore or a keystore with multiple aliases.
For example you could do this in the signing configuration:... <validity>${signing.validity}</validity> <dnameCn>${signing.dname.cn}</dnameCn> <dnameOu>${signing.dname.ou}</dnameOu> <dnameO>${signing.dname.o}</dnameO> <dnameL>${signing.dname.l}</dnameL> <dnameSt>${signing.dname.st}</dnameSt> <dnameC>${signing.dname.c}</dnameC> ...where the placeholder information is kept in the company super POM. A lot easier to maintain, I reckon.
- The signature will always be valid when signing (no need to replace keystore to remove warnings)
Maven and Ant guys: you’ll never agree. On anything. Period. Deal with it!
Every 3 months or so, you'll see a new article pop up somewhere written by an Ant (mostly, sometimes Gradle or Rake) lover discussing the horrible nature of Maven and how it works.
People, please! How old are you guys? I've used both and I like Maven more. Is that a reason for me to post another rant on Ant? Seriously, most rants are one-way. It's easy to break down a tool based on some criteria, but how about proposing some solutions to problems some Maven users face that you can easily solve in [your favorite buid tool]? Whoops. Negative criticism is easy, isn't it?
Recently, I read this article. It doesn't take a genius to discover that this blogger is a Maven hater. Or to put it more bluntly: it doesn't support the way HE wants to build software. Here's an idea: try and take every feature you hate in Maven and give a Rake or Ant alternative way. Every single one. I'm waiting for the excuses you'll throw at me.
Let's see. According to this blogger, it's really hard to build a war. Last time I built a WAR with Maven, I just needed to change the packaging to WAR. Okay, you do need to know that the WEB-INF folder goes into src/main/webapp. Is that really that hard? The other rant was about the WAR size. Here he actually has a (albeit minor) point. Maven projects (especially frameworks) really need to learn the value of optional dependencies. But then again, I'd like to see Ant projects solve this issue too without their usual solution: provide a README stating which jars should be included when you want to use certain functionalities.
I've heard the statement "Why use an entire toolbox (Maven) when you only need a hammer (Ant)". Okay, you CAN put a screw in a wall with a hammer. The result ain't pretty, but it works. Does that mean a hammer is the only tool to use. No. Know when to use a tool. Any tool for that matter. Would you throw the screwdriver out of your toolbox because it can't handle a nail?
Any of the rants can be translated into either incorrect use of Maven or just plain stupidity (or even lazyness): I can point out a lot of Ant, Gradle or Rake misuse that is equally painful. Dependency resolving takes to much time? Use Artifactory or Nexus internally (it'll even solve some other problems concerning snapshots). Got problems with plugins? Stop omitting the version number in your POM's (or use a company-wide POM). Too many unused transitive dependencies that should be optional? Contact the author. Got a problem with XML POM's? Maven 3 has polyglot functionality.
All I'm saying is: stop ranting. Either come up with non-abstract solutions, or shut up. Ranting is easy. Coming up with solutions using your own solutions is less so.