Recently, I've been telling my friends that I was excited about the Blackberry Priv; a smart phone running Android that had a physical keyboard.
Many years ago, I switched to Sprint and got a Samsung Epic 4G. There are two things about this phone that make it different from most of the smart phones that I've seen my friends and family carrying around:
To make a long story short, WiMax was Sprint's attempt to go their own way on 4G connectivity. Their attempt failed, but it did not fail quietly. As for me, I connected to the WiMax 4G network a single time, during a trip to Chicago. Other than this, I never used the 4G network. That's probably why, I didn't really miss it when Sprint decided to shut it down.
So why not get another phone that supported 4G LTE?
Good question! I'm glad that you asked! The answer is: I LOVE the physical keyboard in my Epic 4G. And it's not that I use it every day, or even every month. It's more like a snow shovel; it may sit unused for 9 months, but when I want to use it, I am very happy that I have it available.
Most smart phones don't have physical keyboards. I chalk this up to simple free market capitalism: physical keyboards cost more to make than customers are willing to pay extra to have. It seems that I am part of a minority when it comes to physical keyboards. I'm willing to pay extra, but most people are not.
So, I was very excited the first time that I saw a picture of the Blackberry Priv. A modern smartphone, running Android, that was going to have a nice physical keyboard! I could finally ditch my Samsung Epic 4G (running Android 2.3!) for a modern Android! Yes!
... the Slashdot headline points out "The company may see this as a way to differentiate themselves from the competition."
Indeed, it has differentiated itself from the competition in one very important way. Their competitors, offering products with Zero Knowledge Encryption, are products that I want to buy. Blackberry is offering products that I do not want to buy.
Am I worried about the government getting a warrant and looking through my phone? No. I'm a pretty boring person. My phone usage is mostly SMS messages to figure out when I'm supposed to pick somebody up, or what else I'm supposed to buy at the grocery store while I'm there.
The problem is, that by definition, lawful interception requires the capability to intercept (period). This means that devices can be divided into two types:
- Devices that cannot be intercepted
- Devices that can be intercepted
Or, to write that another way:
- Devices that are secure (cannot be intercepted)
- Devices that are not secure (can be intercepted)
If Blackberry has my encryption keys to give to law enforcement on demand, then by definition they have my encryption keys. They can also...
Accidentally give them to law enforcement when a proper warrant hasn't been obtained.
Accidentally give them to someone posing as law enforcement; with or without fake warrants to back up their story.
Accidentally give them to hackers who break into Blackberry servers.
Accidentally leak them to the public at large by leaving them on an insecure server that gets indexed by Google or another search engine.
Have a rogue employee steal them and sell them on the black market; maybe some bad guy can now use my phone to send a message without me even knowing about it!
Have a regular employee mess up and accidentally send my keys in response to a lawful request for somebody else's information.
Etc...
You know what prevents all of these scenarios?
Not holding your customer's encryption keys, at all, for any reason.
(i.e.: The way Apple now does it.)
So... Sorry Blackberry. You almost had a customer for the Blackberry Priv.
But, I don't buy products that are defective by design.
In about a month, it will be my 36th birthday. Normally, my birthday isn't a big deal to me. I get a few Happy Birthday e-mails, my girlfriend makes a chocolate cake; it's all low-key and nice.
This year is a little different. My driver's license expires, and I need to stop in at a Department of Motor Vehicles (DMV) in order to get a new license. So, you might ask, what's the big deal? Show up, fill out a form, pay the fee, and get a new license. Easy as cake, right? Not quite.
Religious Conviction
I am a Pastafarian. I am also an ordained minister of the Church of the Flying Spaghetti Monster. When it comes to getting a driver's license photo, the tradition is to wear a spaghetti strainer (colander) upon one's head for the photo.
Here are some other examples of Pastafarians who have done the same in the past two years:
This is the first time I've needed to update my driver's license photo since I became a Pastafarian. I don't know how much resistance to expect from the Wisconsin DMV. Aaron Williams was denied his right to wear a religious headware by the New Jersey DMV in Feb 2013. It seems other Pastafarians in other states have fared better since then. I remain hopeful.
Sincere Beliefs
My girlfriend of two years doesn't understand my desire to wear a colander on my head for my driver's license photo. Her objections are similar to objections that my friends have voiced before:
A driver's license photo is serious, why aren't you taking it seriously?
Why do Pastafarians make fun of religion and religious people?
Juxtaposed, the two objections make an odd paradox. Other religions are assumed to be sincerely held by the people who practice them, they are serious, and it is disrespectful to make fun of them. Yet, if I seek to practice my religion, I'm assumed to be insincere, making fun of religion, and not taking my driver's license photo seriously. It's really an odd kind of contradiction.
It occurs to me that my girlfriend (a non-Pastafarian) does not understand the nature and purpose of the Church of the Flying Spaghetti Monster. She does not understand that I am a Pastafarian and therefore, I am Living the Question.
Asking the Question
Before we get to Living the Question, it might first help to Ask the Question. Before we Ask the Question, we need to ponder something important. Here, I need to quote the Join Us page of the Church of the Flying Spaghetti Monster:
"FSM is a real, legitimate religion, as much as any other. The fact that many see this is as a satirical religion doesn’t change the fact that by any standard one can come up with, our religion is as legitimate as any other. And *that* is the point."
What I will ask you to ponder is this: The Pastafarian religion is a legitimate religion, but many see it as a satirical religion. If we want to be taken seriously, why go to all the trouble to make our religion appear satirical to so many? Is it important to Pastafarians that our religion be seen as satire?
The reason for this becomes clear after we Ask The Question. The Question, succinctly, is this:
What is the proper role of society with respect to religion?
That's a pretty big question, so let's rephrase that question another way. American society is an inclusive society with multiple religions. Our constitution guarantees Freedom of Religion for all. Most of us belong to one religion and regard the other religions as strange. So the question is:
How does society maximize the religious freedom of everyone, while preserving its own legitimate interest in the welfare of its citizens?
This turns out to be a very important question. For example:
There are almost too many questions to enumerate them all. Now we see why The Question is such a big and important question. So let's ask it again, so that we're clear on The Question...
What is the proper role of society with respect to religion?
Living the Question
Let's return to what we were pondering before:
The Pastafarian religion is a
legitimate religion, but many see it as a satirical religion. If we want
to be taken seriously, why go to all the trouble to make our religion
appear satirical to so many? Is it important to Pastafarians that our
religion be seen as satire?
Now in the context of The Question:
What is the proper role of society with respect to religion?
Let's consider this another way:
What is society to do when a very strange religion, with very strange beliefs shows up? What if some of those beliefs seem to be absolute nonsense and at odds with reality? What if some of those beliefs are based on logical fallacies and other faulty reasoning?
Now the pieces of the puzzle begin to click together. In asking The Question, we want to know how society should interact with religion. A religion that we are all familiar with is less useful here. Such a religion might be accepted because "Well, that's the way it's always been." which implies a bias against other religions, since they aren't "the way it's always been."
No, to explore The Question, we need a religion that is very odd. A religion whose practices are so far "out there" as to seem like satire. A religion that is so strange that people looking at it from the outside will say to themselves, "Only an idiot would believe that!" And that, is the point.
Our religion is as legitimate as any other, and "other" enough that society can reason about it without personal or cultural biases. Someone might think teaching Creation Science or Intelligent Design in public schools is a good idea. Yet, when asked if schools should teach Flying Spaghetti Monsterism, they don't have the pro-FSM bias. Suddenly, they are left in the awkward position of explaining how and why the government should be favoring one religion over another religion.
As a Pastafarian, I am Living the Question when I put a colander on my head for a driver's license photo. I am asking society at large to ask itself, "Should the state make headware exemptions for religious belief?" Is an identification photo with uncovered head important? Does the state have an compelling interest in regulating the appearance of the head in an identification photo? Is that compelling interest so strong as to override religious belief? Or can society tolerate that a strange person with a strange belief wears a strange thing on their (strange) head while having an identification photo taken?
I assert that this is a very important thing for all of us, as a society, as a whole, to consider. Yes, the colander, as part of the bigger picture; The Question:
What is the proper role of society with respect to religion?
I sincerely believe that this is a very important question to answer. And I believe that being a Pastafarian is a good (and fun!) way to help people answer that question.
Now, I just need to find a spaghetti strainer that doesn't make me look fat in the picture.
I recently wrote a high quality, low grade, server side WebSocket (RFC 6455) implementation. The intended use-case is as follows:
I want to create a service in Java that accepts connections from modern web browsers.
I want to use InputStream and OutputStream to get my bytes back and forth between my service and the browser.
I want my service code to do this with minimal setup.
I want to ignore all the protocol details; just give me the bytes.
I'm not sure, but I might like to support SSL/TLS security (i.e.: HTTPS) for none/some/all of my connections.
Need More?
If you need something else, like Java NIO support, non-blocking I/O, evented I/O, ability to provide custom handlers for web frames, etc.. then you'll probably need to find another implementation:
Note that JavaEE 7 includes WebSocket support. So if you've already got a web container (i.e.: Tomcat), then you've probably already got a solution at hand:
This is how you start listening for WebSocket connections:
ServerSocket serverSocket = new ServerSocket(PORT);
WebSocketServerSocket webSocketServerSocket = new WebSocketServerSocket(serverSocket);
WebSocket webSocket = webSocketServerSocket.accept();
And this is how you read data from the connecting client:
InputStream is = webSocket.getInputStream();
int data = is.read();
And this is how you communicate back to the connecting client:
WebSocketServerOutputStream os = webSocket.getOutputStream();
os.writeString("This is a UTF-8 string.");
os.writeBinary("Some binary data.".getBytes());
That's It
A high quality, low grade, server-side WebSocket component with no frills or nonsense. That's it, that's all. Enjoy!
I love Node.js. I love CoffeeScript. I have never found it easier to translate concepts in my head to working code, as I do when I'm working with these two. Like this morning, when I was spinning up a new project...
Node.js Background
In Node.js, a project is defined by a package.json file. Here is an abbreviated example from my project telnet-stream:
Note the section "devDependencies". This section, along with a section called "dependencies" define the dependencies for a Node.js project. The Node Package Manager (npm) will fetch and recursively install all of your project's dependencies with the following command:
npm install
I've discovered two interesting points about the dependency management here.
One, the semantic versioning is very useful. Note above how I've specified "coffee-script": "1.7.x". When npm installs the coffee-script module, it will automatically grab the latest in the 1.7 series. And that is just scratching the surface of what you can specify with semantic versioning.
Two, you can specify Git URLs as dependencies, including your preferred branch/tag. Need to make a small tweak to a dependency? Easy, clone the repository, make your tweak, and specify the URL to your branch. Managing bleeding edge dependencies couldn't be easier.
Even better, as of version 1.1.65, GitHub URLs can be specified as dependencies using an abbreviated "user/project" syntax. So, if you wanted to use my telnet-stream project:
If you use GitHub, this is a very convenient way to manage dependencies for and between your projects.
CoffeeScript Background
CoffeeScript is a language that transpiles into JavaScript. That is, you write code in CoffeeScript and the compiler will output JavaScript. Said JavaScript is suitable for running in the browser, or on the server in containers like Node.js.
One of the nice little utilities provided by CoffeeScript is cake. cake is a simple build utility, similar to make; instead of a Makefile, you specify a Cakefile. Unlike a Makefile, a Cakefile has no special format. It is just a CoffeeScript source file with a few helper functions to define tasks.
For example, here is my Cakefile for telnet-stream:
{exec} = require 'child_process'
task 'build', 'Build the module', -> compile -> test()
task 'clean', 'Remove build cruft', -> clean()
task 'compile', 'Compile CoffeeScript to JavaScript', -> compile()
I've defined a lot of tasks at the top (build, clean, compile, rebuild, test), but none of those are required. The Cakefile is plain old CoffeeScript, so everything is optional. Note that the five tasks delegate to only three functions that call exec.
Problem
So this morning, I was specifying the dependencies for my project in package.json. This is a project that I intend to deploy for public consumption on the Internet, so stability and managed configuration are important. One of the things that caught my eye was the semantic versioning:
If I were to execute npm install today, npm would choose CoffeeScript 1.7.1 to satisfy the "coffee-script": "1.7.x" dependency. However, if I were to install next month (or next year), there may be a new version of CoffeeScript in the npm repository. Installing at that time might give me CoffeeScript 1.7.8.
The convention is that minor point changes shouldn't affect the public API. That is, the difference between 1.7.1 and 1.7.8 might be internal bug fixes or optimizations. We do not expect that major components in the module will be removed, renamed, or exhibit wildly different behavior.
Because this is intended to be a production-grade service, a managed configuration is important. Or: It is not wise to bet the behavior of the service on how well a dozen+ authors of third-party modules can follow the versioning conventions. Despite the power of npm's semantic versioning, it is unwise to trust it with the final configuration of a public facing production-grade service.
For the project, this means the dependencies have to be specified precisely. If we say 0.1.6, we mean, version 0.1.6 and no other. This solves the managed configuration problem, but it does open some other problems:
How can we be sure that we're not missing bug fixes, especially security related bugs?
How can we be sure our codebase evolving with its components, and staying on the cutting edge of their features?
Just because our configuration is managed doesn't mean that it can't change or evolve. The key here is that configuration changes shouldn't be a surprise. It is okay to use version 0.1.7 instead of version 0.1.6, if you've reviewed the changes and know what differences to expect in production.
We just need a tool to tell us which dependencies have been updated and require review...
Solution
I thought that it would be neat if I could look up my project's dependencies and determine if I've specified the latest version or not. So I wrote a Cakefile task to do that:
{exec} = require 'child_process'
task 'depcheck', 'Check dependency versions', -> project = require './package.json' for dependency of project.dependencies checkVersion dependency, project.dependencies[dependency] for dependency of project.devDependencies checkVersion dependency, project.devDependencies[dependency] checkVersion = (dependency, version) -> exec "npm --json info #{dependency}", (err, stdout, stderr) -> depInfo = JSON.parse stdout if depInfo['dist-tags'].latest isnt version console.log "[OLD] #{dependency} is out of date #{version} vs. #{depInfo['dist-tags'].latest}"
And here is the output of a contrived example:
tux@laptop ~/NetBeansProjects/sekrit-project $ cake depcheck [OLD] should is out of date 3.3.0 vs. 4.0.4 [OLD] sansa is out of date 0.1.4 vs. 0.1.6 [OLD] express is out of date 4.4.1 vs. 4.4.3 [OLD] body-parser is out of date 1.2.1 vs. 1.3.1 [OLD] supertest is out of date 0.11.2 vs. 0.13.0 [OLD] mocha is out of date 1.18.0 vs. 1.20.1 [OLD] coffee-script is out of date 1.7.0 vs. 1.7.1
A dozen lines of CoffeeScript and now a single command tells me if any of my project's dependencies are out of date. Did I mention that I love Node.js and I love CoffeeScript?
Thoughts
What if I wanted to do something like this with Java and Maven?
I stayed up rather late writing some code for my MUD. When I finally went to sleep, I had an interesting dream involving a conversation with the idealized personification of MongoDB.
What the heck? How can one have a conversation with a NoSQL document store? Have you ever seen Transformers: The Movie (1986)? Do you remember the scene where Megatron encounters Unicron?
If you imagine that I was Megatron and MongoDB was Unicron, you won't be far off. However, our conversation was a little less contentious than the one Megatron and Unicron shared.
After I woke up, some of that conversation congealed into a cryptography idea.
Bath-Salted Password Hashing
If the idea turns out to be a good one, then I decided to call it Bath-Salted Password Hashing because it will make password crackers as angry as someone high on bath salts.
If the idea turns out to be a bad one, then people will say I must have come up with it while high on bath salts.
Note: I'm not sure if this idea has been proposed before. I've done some reading on salted password hashes, but I don't remember this proposal. Likely, it has been proposed and shot down as insecure before. If you have experience with cryptography or especially cryptanalysis, I would appreciate your feedback!
Password Storage - The Naive Approach
The problem we are trying to solve (and the problem I was working on with my MUD) is the problem of storing passwords for authentication. You've got a service hosted online and your users have an account registered with the service. You want to make sure that an account is only accessed by users who know a shared secret (i.e.: the password to the account).
So in your database, you store the account name and you store the password in plain text. When someone logs in, you check to see that the password they provided matches with what you have stored in your database.
Account
Password
Alice
Michelle
Bob
bobrulz
Carol
DybubyeaHyd9
Dave
bobsux
This is simple and it works. However, there is a serious drawback to this solution: Every password is stored in plain text. One security breach and every account is instantly compromised. Even worse, people re-use their passwords on multiple services, and so all of those accounts are now compromised too.
Password Storage - The Slightly Better Approach
Instead of storing the password, we decide to store the hash of the password. A hash function is a mathematical function that scrambles up the input and attempts to provides a more-or-less unique value, a kind of fingerprint of the input if you will.
Account
Password
Alice
ee17dc479d8eb9848d89de4ae67b526d
Bob
75ecf6c2761847d6bd1d356f3c4fafcd
Carol
883380925f05df121f930e5fb7e27ced
Dave
052f13c55882109205f02c240132259c
So now when a user provides their password, we run it through the hash function and compare the output. If they match, they've provided the same password and we grant access. If not, we deny them access to the account.
Surely this is much better. If we have a security breach, the attacker can't see the passwords and the day is saved. Or, is it...?
A quick search for the hash output gives us Alice's password and Dave's password. Well, that's certainly less than ideal.
Password Storage - The Somewhat Better Approach
I don't know if you've seen The Shield. It was an excellent television show (cop drama) that ran from 2002 to 2008. I watched it up until I moved to India in 2004 and I am just now (5 years on) trying to catch up to where I left off.
In The Shield, there was a scene in Season 2 where Officer Shane Vandrell tells Officer Julian Lowe, "When it comes to your partner's career, the truth is like grits. You don't serve it up plain, you put a little salt on it."
To paraphrase Shane Vandrell:
When it comes to hash functions, passwords are like grits. You don't serve it up plain, you put a little salt on it.
A salt is a bit of random data that you mix with the password before providing it to the hash function. The output then reflects the combination of the password and the salt value. So in our database, we'll generate a random salt for everybody and store it alongside the password.
Account
Salt
Password
Alice
PeksOtt6
2566bb01cdf46637d85c314ca74800c5
Bob
Jagwelv4
413c9255f6df8b2b5d10b8cdf773d3c3
Carol
AvTohon1
f6010835686677d7540b75e3a78be0f4
Dave
topNeum4
0b6c8acd08fa79230c589d75b9974df2
When a user attempts to log in, we look up their salt value, append the provided password and feed that into the hash function. If the output matches, we grant access to the account. If not, we deny access to the account.
But what about a security breach? Try searching for those hash values. As of this writing, they don't lead to any results. (Of course, if Google decides to index this blog post, that'll blow this paragraph straight to hell, won't it?) So the day is saved. Or is it...?
As you might guess, even salt is not the end of the story. If we use a simple scheme to salt the password (such as appending the password to a salt value) we make it harder for the attacker to simply look up the password.
However, this does not thwart clever attempts to guess the password by brute force. If an attacker knows that Dave is a high value account (maybe Dave is an administrator on the service and has access to special commands) they would start by computing the hash of the salt:
startingPoint = MD5("topNeum4")
Now they try to brute force Dave's password. Unfortunately, Dave has a rather short password ("bobsux") and it probably won't be long before it is broken.
You might ask: What if we add some more salt? Put some salt on the end of the password? Or in the middle of the password? Will that help?
Yes and no. Salt is not considered to be a secret. The prefix salt can still be used as a starting point, and salt in the middle or at the end just adds a few more computational steps in the cracking process. Here's an interesting article that goes into some more detail on that:
So yes, it helps to slow down the attacker a bit. But no, it doesn't provide an order of magnitude more help.
Password Storage - The Current State of the Art
Password-Based Key Derivation Function 2 (PBKDF2) uses several thousands of hash iterations. By applying the hash function repeatedly, the computation resources needed to hash a single password grows quite large.
By estimating the computing resources of the attacker, we choose the number of iterations sufficiently high that testing even a small part of the password space is infeasible.
However, as the Wikipedia article points out, even PBKDF2 can be misused. A single iteration is really no stronger than a single hash. So if you intend to use PBKDF2, make sure that you use it correctly.
Wikipedia also references bcrypt and scrypt as members of this key expansion / key stretching family of salted password hashing algorithms.
Password Storage - Bath-Salted Password Hashing
You're still here? Oh good. This is where we get to the meat of this blog post.
I propose a "new" (at least to me) method of hashing salted passwords. I don't know if it is more secure than existing methods. I don't know if it requires more resources to crack than existing methods. Literally, this idea congealed in my head less than 24 hours ago. I write this post with two goals:
Communicate the Bath-Salted Password Hashing algorithm so that anybody could implement it in the language of their choice.
Solicit feedback from cryptographers: Is this idea worth considering? Or is it a waste of time for reasons that are currently unknown to me?
Preconditions:
- Given P = a password, N bits in length
- Given X = a prefix salt, at least 2*N bytes in length
- Given Y = a postfix salt, at least 2*N bytes in length
- Given H = a strong hash function (for example, SHA-3 [as of this writing])
- Given I = an empty input buffer
Pseudo-Code Algorithm:
x = 0 y = 0 for i = 0 to (N-1) b = P.bit(i) if b == 0 I.push(X[x]) x++ else // b == 1 I.push(Y[y]) y++ I.push(X.slice(x,X.length)) I.push(P) I.push(Y.slice(y,Y.length))
output = H(I)
Description:
For each bit of the password, we consume a byte of either the prefix salt or the postfix salt, depending on the value of the bit. The byte is appended to the input buffer.
After we have consumed N bytes (taken in an arbitrary order from the two salt values) we append the remainder of the prefix salt to the input buffer.
We then append the password to the input buffer.
We then append the remainder of the postfix salt to the input buffer.
The output is then the result of running the hash function on the input buffer.
Discussion:
The bits of the password are used to select the salt bytes that are used to construct the input buffer. Because the password could have an arbitrary length and bit content, the number of bytes, where they came from, the order in which they will appear will vary significantly between passwords.
Although the salt is not secret, the portion of it that becomes more password-like than salt-like is unknown to the attacker.
A possible improvement would be to run the password (unsalted) through a second distinct hash-function (call it H') to obtain P' and then truncate the number of bits to match the number of bits in the original password. The idea here would be to take a password with a (potentially guessable) lopsided number of 0s and 1s and convert it to something with a more even distribution of 0s and 1s. We truncate to retain the original bit length so as to deprive the attacker the knowledge of a fixed consumption of the salt values.
Iterated Bath-Salted Password Hashing?
I'll admit that I haven't given much thought to iteration yet. But here's sketch of how I might start exploring it:
Let A be the number of iterations to run the password through H' during each iteration of the bath-salt algorithm.
For example, if A=5 then the inner-hash would be P' = H'(H'(H'(H'(H'(P)))))
Let B be the number of iterations to run the bath-salt algorithm
After computing the output of a bath-salt iteration, let Mutate_Index be the (log2(N)+1) bits from the LSB of the output. Mutate (xor 1) the bit at (index % N) of P. Now iterate B-1 more times.
More To Come
Unfortunately, I've already spent more time writing this blog post than I intended. My plan is to follow up next week with more specifics, including a Node / JavaScript / CoffeeScript implementation of these ideas.
Thank you for reading and many thanks in advance for any feedback!
In my previous post on the topic of Using Guava EventBus with Spring, several smart people pointed out shortcomings in my code sample.
Yvan DeBoeck pointed out that Guava was already doing the work of scanning an object for @Subscribe annotations. So, I reduced that aspect in my code. My scanning is now limited to reporting a warning in the logs, and is only performed on beans that I am not registering with EventBus.
Benjamin Diedrichsen pointed out that EventBus uses strong references. This is a really important tip! Spring will happily generate plenty of non-singleton beans, and giving them to EventBus is a recipe for a memory leak. So, I added an @Autowired reference to the ApplicationContext and use that to verify if a bean is a singleton before calling eventBus.register().
Here is my updated version of EventBusPostProcessor:
/*
* EventBusPostProcessor.java
* Author: Patrick Meade
*
* EventBusPostProcessor.java is hereby placed into the public domain.
* Use it as you see fit, and entirely at your own risk.
*/
package com.example.spring.guava;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import java.lang.reflect.Method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
/**
* EventBusPostProcessor registers Spring beans with EventBus.
* @author pmeade
*/
public class EventBusPostProcessor implements BeanPostProcessor
{
private static final Logger log = LoggerFactory.getLogger(EventBusPostProcessor.class);
public static boolean containsSubscribe(Object bean)
{
Method[] methods = bean.getClass().getMethods();
for(Method method : methods)
{
Subscribe subscribe = method.getAnnotation(Subscribe.class);
if(subscribe != null)
{
return true;
}
}
return false;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException
{
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException
{
if(applicationContext.isSingleton(beanName)) {
eventBus.register(bean);
} else {
if(containsSubscribe(bean)) {
log.warn("Bean {} containing @Subscribe annotation(s) was not "
+ "registered with EventBus. EventBus registration of "
+ "prototype beans (isSingleton() == false) can cause "
+ "memory leaks.", beanName);
}
}
return bean;
}
@Autowired private ApplicationContext applicationContext;
@Autowired private EventBus eventBus;
}
Recently, I started working on VeloxMUD again. The time away has really given me a new perspective on the code. Usually, that perspective goes like this:
What the heck was I thinking?
The first thing I looked at was the code that handled connections for the MUD. There is a thread that waits on socket.accept() and then passes the Socket objects off to a handler service (injected by Spring). Although this isn't terrible, I thought I could improve it by using a Publish-Subscribe pattern to decouple the two.
I started my search with Observable and found that it wasn't going to fit. Although the Observer interface isn't bad, the need to derive from Observable wasn't going to work for me. My publisher (the "observable" object) already derived from Thread. And it seemed there had to be a better way.
I stumbled upon Guava's EventBus. Ah ha! This is what I need! But wait, where are all the publish-subscribe guts that I need to implement? Gone... Publishers post events of any arbitrary type, subscribers add a method (with any name) and a @Subscribe annotation. Done. Wow! This is observable done right!
Oops... one small problem. Most of the stuff that needs to register is created and managed by Spring. How do I register my subscribers without tackling all sorts of nasty lifecycle problems?
BeanPostProcessor comes to the rescue! We can provide custom bean-processing code to Spring. With the right piece of code, Spring can do all the necessary registration for us.
/*
* EventBusPostProcessor.java
* Author: Patrick Meade
*
* EventBusPostProcessor.java is hereby placed into the public domain.
* Use it as you see fit, and entirely at your own risk.
*/