Revisiting Guice and AOP with AspectJ
I decided to revisit my article on Guice and AOP with AspectJ instead of the AOP Alliance API that Guice comes with.
The full source code is available on GitHub.
To refresh your memory, we had implemented a declarative approach for controlling the access to a class methods through annotations:
@WithUserProfileVerification public class InMemoryContactManager implements ContactManager { private final Set<Person> contacts = new HashSet<Person>(); @RequiresProfile(ADMIN) public ContactManager add(Person person) { contacts.add(person); return this; } @RequiresProfile(ADMIN) public ContactManager remove(Person person) { contacts.remove(person); return this; } @RequiresProfile(USER) public Person lookup(String name) { for (Person person : contacts) { if (person.getName().equals(name)) { return person; } } return null; } }
First off AspectJ is way more expressive than the AOP Alliance API. Using Guice with AspectJ is not very different. If your aspects do not need injection then you fall back to plain AspectJ development, as Guice and AspectJ live apart from each other. Things are a little more subtile if you need to connect Guice with your aspects.
In our case we had one aspect that needed injection, hence the trick is to ask Guice to perform injection on the aspect instance. By default, an AspectJ aspect is a singleton, so all we need is to grab an access to the instance, which is as simple as calling the Aspects#aspectOf() static method.
Now let's see some code that differs from the Guice / AOP Alliance sample I showed you.
Here is how you can define a simple dirty console logger that hooks itself onto any implementation of the ContactManager interface:
package info.ponge.julien.hacks.guiceaspectj.aspects; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class ContactManagerLogger { @Before("call( * info.ponge.julien.hacks.guiceaspectj.contact.ContactManager.*(..) )") public void methodCalled(JoinPoint thisJoinPoint) { System.out.println("Calling: " + thisJoinPoint.getSignature().getName()); } }
The aspect that checks for the user permissions is implemented as follows:
package info.ponge.julien.hacks.guiceaspectj.aspects; import com.google.inject.Inject; import info.ponge.julien.hacks.guiceaspectj.auth.RequiresProfile; import info.ponge.julien.hacks.guiceaspectj.auth.UserProfile; import info.ponge.julien.hacks.guiceaspectj.auth.UserProfileChecker; import info.ponge.julien.hacks.guiceaspectj.auth.WithUserProfileVerification; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import static info.ponge.julien.hacks.guiceaspectj.auth.UserProfile.ADMIN; import static info.ponge.julien.hacks.guiceaspectj.auth.UserProfile.USER; @Aspect public class ProfileVerification { @Inject UserProfileChecker userProfileChecker; @Before("execution( * *(..) ) && @annotation( required ) && within( @WithUserProfileVerification * )") public void verify(RequiresProfile required) { UserProfile expected = required.value(); UserProfile current = userProfileChecker.getCurrentUserProfile(); if (insufficientProfile(expected, current)) { throw new RuntimeException("The current user profile (" + current + ") is not sufficient: " + required); } } private boolean insufficientProfile(UserProfile required, UserProfile current) { return (required == ADMIN && current != ADMIN) || (required == USER && (current != USER && current != ADMIN)); } }
Finally our Guice module is configured to perform the injection on the singleton that corresponds to the previous aspect:
package info.ponge.julien.hacks.guiceaspectj; import com.google.inject.*; import info.ponge.julien.hacks.guiceaspectj.aspects.ProfileVerification; import info.ponge.julien.hacks.guiceaspectj.auth.UserProfileChecker; import info.ponge.julien.hacks.guiceaspectj.auth.dumb.DumbUserProfileChecker; import info.ponge.julien.hacks.guiceaspectj.contact.ContactManager; import info.ponge.julien.hacks.guiceaspectj.contact.Person; import info.ponge.julien.hacks.guiceaspectj.contact.simple.InMemoryContactManager; import org.aspectj.lang.Aspects; import static org.aspectj.lang.Aspects.*; public class Main { public static void main(String[] args) { new Main().execute(); } private Module guiceModule = new AbstractModule() { @Override protected void configure() { bind(ContactManager.class) .to(InMemoryContactManager.class) .in(Singleton.class); bind(UserProfileChecker.class) .to(DumbUserProfileChecker.class) .in(Singleton.class); requestInjection(aspectOf(ProfileVerification.class)); } }; private Injector injector = Guice.createInjector(guiceModule); private void execute() { ContactManager contacts = injector.getInstance(ContactManager.class); UserProfileChecker profileChecker = injector.getInstance(UserProfileChecker.class); profileChecker.login("Julien", "secret"); contacts.add(new Person("Julien Ponge", "julien.ponge@gmail.com")); contacts.add(new Person("Jean-Jacques", "jean.jacques@gmail.com")); profileChecker.logout(); profileChecker.login("Jean-Jacques", "1234"); System.out.println(contacts.lookup("Julien Ponge")); profileChecker.logout(); contacts.add(new Person("Mr Bean", "mrbean@gmail.com")); } }
As one would expect, trying to add Mr Bean fails due to insufficient permissions:
Calling: add Calling: add Calling: lookup Julien PongeCalling: add Exception in thread "main" java.lang.RuntimeException: The current user profile (ANONYMOUS) is not sufficient: @info.ponge.julien.hacks.guiceaspectj.auth.RequiresProfile(value=ADMIN) at info.ponge.julien.hacks.guiceaspectj.aspects.ProfileVerification.verify(ProfileVerification.java:27) at info.ponge.julien.hacks.guiceaspectj.contact.simple.InMemoryContactManager.add(InMemoryContactManager.java:21) at info.ponge.julien.hacks.guiceaspectj.Main.execute(Main.java:54) at info.ponge.julien.hacks.guiceaspectj.Main.main(Main.java:17)
Watch the IzPack commits history… on video!
Thanks to the awesome Gource project, you can generate a video from your favorite project commits history!
I just did that on IzPack:
IzPack Gource Video 2010-02-09 from Julien Ponge on Vimeo.
Note that IzPack did not use CVS in the beginning, hence the video starts in 2002.
Enjoy
How I use DropBox
On popular request, here is how I use DropBox. In case you had never heard about it, DropBox gives you a "web folder" (a-la Mobile.me) which is synchronized between your different machines. DropBox works on Mac OS X, Windows and Linux. You can use it to share files with friends too (which is useful for sharing... well... Linux distros and Stallman-compliant software).
You can get a 2 GB free account which is way sufficient for most usages, but switching to a paid account is certainly a good idea if you can afford to. I should also mention that the service is rock-solid, and the software clients work seamlessly in the background.
There are a bunch of files that I need to share between my laptops at home and at work, and DropBox is just way better than a USB key or a geeky rsync-based solution (or worse, a Subversion-based one). The only thing is that I never store sensitive informations in my DropBox folder, as this data is to be hosted in the clound, and you never know what may happen to it.
Whenever we need to exchange stuff with friends, we simply drop something into our shared archive. It can't really be any simpler. Another nicety about DropBox is that it stores revisions of your files, so that you can always revert a deletion or switch back to a previous version. It is not as powerful as a true version control system, but it does a fine job as long as you spot where the option is (only in the web interface).
But the true geeky usage that I have is when combined with Git (or any other DVCS of your choice). The power of DVCS lies in the fact that they store the whole history locally and as such, they do not require a server. You can thus store your Git branches in a DropBox folder, or put a symlink in it. This way you get the benefits of both DropBox and Git (or Hg, Bzr, ...): powerful VCS, automatic remote backups and synchronization between workstations.
How about yourself? How do you use DropBox?
Soundcloud
I have recently discovered Soundcloud, a very fine platform for hosting audio productions.
Among the things that I liked is the very nice Flash player that they have:

