Tx over actions

DEPRECATED! This page has not been updated for a long time. Still, it may contain some valid and valuable information.

Up to now we have used something what is consider as 'default' layout for three-tier web application. Actions uses services, transaction is started, services uses DAOs, result is returned.

If we say that unit-of-work is what happens on one (user) action, we will come to following situation: all actions will more-less delegates to service layer. Calling more than one service method from one action method is not totally correct: on unit-of-work should be wrapped with single transaction (unless done differently on purpose). Even if we are just reading, this should be done in one transaction, as one unit-of-work. There are two consequences from this approach: web actions code is dull and often one service has to return more results at once.

Lets take different approach to make development more pragmatic. Since Madvoc actions are POJOs, we can say that our service starts with action's method invocation that stores results in the action object instead of returning them. So, Madvoc itself will be a presentation layer, not our actions. Our previous service layer now becomes a fine-grained business layer with 'thinner' functions that can be combined together to make some real work done.

In practice this means that we have to enable transactions over action invocation.


One idea would be to use some transactional interceptor over actions. However, this would not work out from web container - in our new approach, interceptors are part of the presentation layer. So we will do the same what we did with the service layer: add @Transaction annotation over action methods.

The first change is, obviously, on Proxetta aspect definition (in AppCore): now we need to include action classes as well:

    ProxyAspect txServiceProxy = new ProxyAspect(AnnotationTxAdvice.class,
        new MethodAnnotationPointcut(Transaction.class) {
            public boolean apply(MethodInfo methodInfo) {
                        isPublic(methodInfo) &&
                        isTopLevelMethod(methodInfo) &&
                        (matchClassName(methodInfo, "*Service") ||
                        matchClassName(methodInfo, "*Action")) &&

Now lets make Madvoc to apply proxy before some action class is registered. Obviously, we will do that on action registration. Here we have to be careful; we want to create only one action proxy class for all defined action methods. So, when some action (method) is registered we have to check if its class is already proxified. For that purpose we will create ProxettaAwareActionsManager component, that might looks like this:

    public class ProxettaAwareActionsManager extends ActionsManager {

        protected final Proxetta proxetta;
        protected final Map<Class, Class> proxyActionClasses;

        public ProxettaAwareActionsManager() {
        public ProxettaAwareActionsManager(Proxetta proxetta) {
            this.proxetta = proxetta;
            this.proxyActionClasses = new HashMap<Class, Class>();

        protected synchronized void registerAction(
                Class actionClass, Method actionMethod, String actionPath) {
            if (proxetta != null) {
                // create action path from existing class (if not already exist)
                if (actionPath == null) {
                    ActionConfig cfg = actionMethodParser.parse(
                            actionClass, actionMethod, actionPath);
                    actionPath = cfg.actionPath;
                // create proxy for action class if not already created
                Class existing = proxyActionClasses.get(actionClass);
                if (existing == null) {
                    existing = proxetta.defineProxy(actionClass);
                    proxyActionClasses.put(actionClass, existing);
                actionClass = existing;
            super.registerAction(actionClass, actionMethod, actionPath);

Before applying proxy we have to resolve the action path if not already set. Action path is parsed from class, therefore this has to be done before proxy is created. Further, proxy has to be applied only on classes that are not already proxified, so we will save all proxies classes and defined proxy only for new ones.

This new Madvoc component has to be registered in standard way in our AppWebApplication:

    public class AppWebApplication extends PetiteWebApplication {
        public void registerMadvocComponents() {
            registerComponent(new ProxettaAwareActionsManager(appCore.proxetta));

Since it is extending default actions manager, it will also automatically replace the existing action manager component.


Just add @Transaction annotation over Madvoc action method. Note that we are still able to declare transactions over services.


LocalRunner will not be able to run services that are not annotated with transaction attribute. Moreover, we should be able to run our new service layer without the container. Therefore, we will create WebRunner, simple class that will now work with AppWebApplication instead of AppCore. Here is how such WebRunner may looks like:

    public class WebRunner {

        public static void main(String[] args) {
            AppWebApplication app = new AppWebApplication();
            IndexAction ia = app.createAction(IndexAction.class);

Our web application AppWebApplication needs to be enhanced with createAction method, so we can create Madvoc actions without the container:

    public <E> E createAction(Class<E> action) {
        if (appCore.proxetta != null) {
            action = appCore.proxetta.defineProxy(action);
        return appCore.petite.createBean(action);