Sebastian Kuligowski's Home Page

AOP in Java Enterprise Applications

November 11th, 2008

After reading this article you will be able to change functionality of your enterprise application written in Java language without recompilation of source code of your application, without stopping application server on which your application is deployed and moreover without having direct access to production server.

All this will be reached using remote and dynamic aspect weaving extending AOP approach. Remote and dynamic aspect weaving was implemented in PROSE (PROgrammable extenSions of sErvices) system. I will show you how to install PROSE system under Windows OS, how to run Apache Tomcat 6 with PROSE and in the end I will show three real aspects written in PROSE which will be woven into web application deployed on Apache Tomcat 6 web application server. All presented examples are practical and ready to compile and deploy. You will also find here sources of created projects which you can import into Eclipse environment, compile and deploy using ant scripts.

User stories

Imagine you work in a big advertising company which is oriented on the market very aggressively. You are responsible for large-scaled J2EE project (AdvWeb) which serves advertisements for about 1,000,000 web sites. You bite your nails because Christmas time is coming and traffic dramatically increases and your boss has a lot of new ideas and requirements. The high availability constraints do not allow you to restart machines every time you change something, moreover it takes for about 15 minutes to start up the system - incurred losses of not served advertisements during these 15 minutes are enormous.

Case 1

Your boss announces you that he wants to collect real-time statistics for requesting words associated with advertisements. In other words he wants to see in logs each requested word with printed total count of its requests.

Case 2

The second case is more difficult. Your task is to bold and change color to red of requested word if it is found in served advertisement. In other words if content of advertisement contains requested word you should change its visual parameters.

Remote and dynamic aspect weaving in action

I will show you how handle these change requests without any change in core application source code, without recompilation of source code and without restarting application server. I will write aspects which will be remotely woven at runtime from remote machine - so you don't need to have access to deployment directories.

Let's create our advertisement application and solve cases using remote and dynamic aspect weaving.

Brief look on AdvWeb application

The application has simple architecture for our purposes. There is one FrontController (AdvController servlet) which handles all requests from web for given word (advertisement word is passed through 'word' parameter of GET method). After every request random Advert object associated with given word is taken from AdvRepository singleton instance. Then specific view is applied on selected advertisement and returned as a String object. Output bytes are written to the requesting browser.

AdvController servlet

package pl.kuligowski.advweb;
 
import java.io.IOException;
 
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class AdvController extends HttpServlet implements Servlet {
   
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
        
        String word = request.getParameter("word");
        
        Advert advert = AdvRepository.getInstance().getAdvertByWord(word);
        AdvView view = AdvView.getChristmasView(advert);
        response.getOutputStream().write(view.getBytes());
    }      
    
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
        
        doGet(request, response);
    }                 
}

AdvRepository singleton

Repository of advertisements creates and holds all advertisements with associated words. Repository object returns randomly chosen advertisement for given word. AdvRepository has been developed as singleton pattern because it is stateless and it provides services for lots of threads.

package pl.kuligowski.advweb;
 
import java.util.ArrayList;
import java.util.Random;
 
public class AdvRepository {
    
    private ArrayList<Advert> adverts;
    
    private Random random;
    
    /**
     * Private constructor registers all advertisements with
     * associated words.
     */
    private AdvRepository() {
        adverts = new ArrayList<Advert>();
        adverts.add(new Advert("gift,monitor,lcd", "Best monitors. Come and buy!"));
        adverts.add(new Advert("buy,christmas,gift,sweet", "Best Christmas gifts!"));
        adverts.add(new Advert("children,joy,cd,gift", "Buy a gift for your child!"));
        adverts.add(new Advert("sweet,jam,honey", "Want to buy sweet products online?"));
        adverts.add(new Advert("dvd,cd,compact,music", "Buy a CD/DVD!"));
        
        random = new Random();
        random.setSeed(System.currentTimeMillis());
    }
    
    /**
     * @param word the word on which advertisement should be returned
     * @return random advertisement
     */
    public Advert getAdvertByWord(String word) {
        ArrayList<Advert> wordAdvertList = getWordAdvertList(word);
        return wordAdvertList.get(random.nextInt(wordAdvertList.size()));
    }
    
    
    /**
     * Collects all advertisements which are associated with
     * defined word. Inefficient but sufficient :)
     */
    private ArrayList<Advert> getWordAdvertList(String word) {
        ArrayList<Advert> wordAdvertList = new ArrayList<Advert>();
        for(Advert advert : adverts) {
            if (wordExists(advert.getWordList(), word)) {
                wordAdvertList.add(advert);
            }
        }
        return (wordAdvertList.size()==0) ? adverts : wordAdvertList;
    }
    
    private boolean wordExists(String[] wordList, String word) {
        for (String advertWord : wordList) {
            if (advertWord.equals(word)) {
                return true;
            }
        }
        return false;
    }
    
    /*
     * Holder for handle of singleton object. It is way to do singleton being 
     * initialized with lazy-init manner and thread safety.
     */
    private static class AdvRepositoryHolder {
        private static AdvRepository repositoryHandle = new AdvRepository();
    }
    
    public static AdvRepository getInstance() {
        return AdvRepositoryHolder.repositoryHandle;
    }
}

Advert POJO

Simple POJO class. Each advertisement is associated with words held in array of String objects. Each Advert has a literal content.

package pl.kuligowski.advweb;
 
public class Advert {
    
    private String[] wordList;
    private String advert;
    
    public Advert(String wordList, String advert) {
        setWordList(wordList);
        setAdvert(advert);
    }
    
    public String[] getWordList() {
        return wordList;
    }
    
    public void setWordList(String wordList) {
        // converting to String array
        this.wordList = wordList.split(",");
    }
    
    public String getAdvert() {
        return advert;
    }
    
    public void setAdvert(String advert) {
        this.advert = advert;
    }
}

AdvView class

AdvView class creates for every request new AdvView instance with wrapped advertisement content. It allows to get content of wrapped advertisement as array of bytes. Using desired static method we decide which view should be applied on the advertisement text.

package pl.kuligowski.advweb;
 
public class AdvView {
 
    private String header; // the header of viewed ad
    private String content; // the content of viewed ad
    private String bottom; // the bottom of viewed ad
    
    private AdvView(String header, String content, String bottom) {
        this.header = header;
        this.content = content;
        this.bottom = bottom;
    }
    
    /**
     * @param advert advertisement passed to be applied with new view
     * @return newly created view
     */
    public static AdvView getChristmasView(Advert advert) {
        return new AdvView(
            "<div><h1 style=\"color:red;\">Christmas advertisement</h1><p>",
            advert.getAdvert(),
            "</p></div>");
    }
    
    public static AdvView getSimpleView(Advert advert) {
        return new AdvView(
            "<div><h1>Advertisement</h1><p>",
            advert.getAdvert(),
            "</p></div>");
    }
 
    /**
     * Constructs final representation of view concatenating
     * three parts of view: header, content and bottom fields. 
     * @return byte array representation of view
     */
    public byte[] getBytes() {
        return (header+content+bottom).getBytes();
    }
}

Deploying

In order to deploy created web application you should compile project and create war file with following structure:

advweb.war/
|--META-INF/
|  |--MANIFEST.MF
|--WEB-INF/
   |--classes/
   |  |--pl/
   |     |--kuligowski/
   |        |--advweb/
   |           |--AdvController.class
   |           |--AdvRepository.class
   |           |--AdvRepository$AdvRepositoryHolder.class
   |           |--AdvView.class
   |           |--Advert.class
   |--web.xml

Don't forget about web.xml file which contains your servlet mapping. Content of web.xml file you will find below.

<?xml version="1.0" encoding="UTF-8"?>
<web-app>
  <display-name>AdvWeb</display-name>
  <servlet>
    <display-name>AdvController</display-name>
    <servlet-name>AdvController</servlet-name>
    <servlet-class>pl.kuligowski.advweb.AdvController</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>AdvController</servlet-name>
    <url-pattern>/AdvController</url-pattern>
  </servlet-mapping>
</web-app>

Place created war file into webapps directory of Tomcat web server, restart the server and open your web browser with site url http://localhost:8080/advweb/AdvController?word=gift. You should see randomly generated advertisement wrapped with Christmas view.

Randomly generated advertisement

Download ready AdvWeb project

If you don't have time or it is waste of time for you making such simple things, download ready war file or source code. Download links are provided below. Project source contains ant script which facilitates building of described project.

Sources (Eclipse-ready): advweb-src-1.0.zip | Web application file (war): advweb.war

Dynamic aspect weaving with PROSE (PROgrammable extenSions of sErvices)

PROSE is a dynamic waving tool wich allows inserting and withdrawing aspects to and from running applications. Early versions of PROSE used for dynamic weaving JVMDI (Java Virtual Machine Debugging Interface). This approeach assumes that each AOP joint-point is converted to stop point. Inserted advice was executed when application stopped having access to context (method parameters and other things).

The next versions of PROSE introduced new approach to dynamic weaving - using JIT compiler. In this approach application is woven with empty joint-points at all possible joint-point locations (at native code) with checking method if any portion of code should be executed.

Current version of PROSE supports also stub and advice weavers wich weave the advices by triggering the recompilation of methods, using the support for code replacement at runtime.

Installing PROSE to Windows system

PROSE is generally platform independent but effort to make it working under Windows is quite big. Thus I will show how I put all installation issues into practice. Let's start.

Installing Cygwin

  1. Download Cygwin and install it with Perl package included - it is very important because PROSE contains a lot of configuration scripts which use Perl language
  2. After installing Cygwin add bin directory to the PATH system environment variable.
    • From the Start Menu, select Start > Settings > Control Panel.
    • Double-click System to open the System Properties window.
    • On the Advanced tab, select environmental variables.
    • Modify PATH system variable adding for example C:/cygwin/bin;
  3. Type cygpath -S in command-line console window. The following output should appear if Cygwin is installed properly:
/cygdrive/c/WINDOWS/system32

Installing PROSE

  1. Download PROSE from PROSE download page and unpack compressed package to the directory without spaces in name (for example C:/prose will be good).
  2. Add PROSE_JAVA_HOME system environment variable pointing installed JDK in your Windows OS
    • From the Start Menu, select Start > Settings > Control Panel.
    • Double-click System to open the System Properties window.
    • On the Advanced tab, select environmental variables.
    • Add PROSE_JAVA_HOME system variable with value for example C:/Java/jdk1.5.0_14 - Note: This is very important to use '/' sign in path. Moreover there should be no ending '/' in the path.
  3. Open new command-line console window and go to C:/prose/pseudo-jre directory.
  4. Type echo %PROSE_JAVA_HOME% command to ensure the system environment variable has been properly set up. If not restart the system or type manually set PROSE_JAVA_HOME=C:/Java/jdk1.5.0_14.
  5. Create PROSE pseudo JRE by typing in opened command-line console window setup-jre command.

After described steps you should have auto-generated pseudo-jre for PROSE which has structure as real JRE but all generated files should have lnk extension. To check if generated JRE is properly installed go to C:/prose/pseudo-jre/bin directory and type java command. The following output should appear:

C:\prose\pseudo-jre\bin>java

C:\prose\pseudo-jre\bin>
prose: *Message* : You are starting jprose from the prose pseudo-jre. This
                   script is named 'java' for convenience only. It indirectly
                   calls the actual java executable and adds significant funct-
                   ionality to java, by allowing run-time weaving using the de-
                   bugger interface (JVMDI). The Java(TM) 2 Runtime Environment
                   Software, complete and unmodified, can be found under
                        C:/Java/jdk1.5.0_14
                   Please read  README.FIRST in /pseudo-jre
not
prose: *Message* Using -Xdebug
Usage: java [-options] class [args...]
(to execute a class)
or java [-options] -jar jarfile [args...]
(to execute a jar file)


(...)

Running Apache Tomcat 6 with PROSE

If you want to use remote and dynamic aspect weaving at runtime your application should be run in JVM extended with PROSE. It can be done by running application with pseudo-jre which we have just prepared.

To enable remote and dynamic weaving in Apache Tomcat 6 do following steps:

  • Copy jdk-jvmai-loc.jar file from c:/prose/lib to C:/apache-tomcat-6.0.14/lib directory (let's assume your Apache Tomcat 6 is installed in C:/apache-tomcat-6.0.14).
  • Open command-line console window and go to C:/apache-tomcat-6.0.14/bin directory
  • Set JRE_HOME environment variable pointing to pseudo-jre prepared before by typing set JRE_HOME=c:/prose/pseudo-jre
  • Set JAVA_OPTS environment variable with remote port of PROSE RMI server to which we could connect to weave our aspects at runtime. Type set JAVA_OPTS=-Dprose.port=5000 to set PROSE RMI server port to 5000.
  • Type startup and press enter to run Apache Tomcat 6 web server with PROSE enabled.

After you perform last step the new command-line console window should appear with Apache Tomcat 6 console output with following content:

prose: *Message* : You are starting jprose from the prose pseudo-jre. This
                   script is named 'java' for convenience only. It indirectly
                   calls the actual java executable and adds significant funct-
                   ionality to java, by allowing run-time weaving using the de-
                   bugger interface (JVMDI). The Java(TM) 2 Runtime Environment
                   Software, complete and unmodified, can be found under
                        C:/Java/jdk1.5.0_14
                   Please read  README.FIRST in /pseudo-jre
not
prose: *Message* Using -Xdebug
prose: *Message* BY SPECIFYING '-Dprose.port=5000' YOU START AN RMI SERVER
THAT ALLOWS UPLOADING ASPECTS IN THIS VM. THE SECURITY
POLICY IN USE BY DEFAULT (c:\prose/lib/testpolicy)
SHOULD NOT BE USED IN A PRODUCTION ENVIRONMENT. IN A
PRODUCTION ENVIRONMENT, PLEASE SPECIFY AN ALTERNATIVE
POLICY USING '-Djava.security.policy'

(...)

2008-01-06 00:44:29 org.apache.catalina.startup.Catalina start
INFO: Server startup in 1894 ms

Enabling PROSE system in Apache Tomcat 6

Starting Apache Tomcat 6 with extended JVM is not enough to weave any aspect at runtime. It is necessary to enable PROSE system by invoking ProseSystem.startup() method. This feature is useful if we want enable PROSE only for example to log specific behaviours of application and disable PROSE after.

Because we do not want to modify existing application deployed on web application server (AdvWeb) we can create new web application only with one servlet which starts PROSE system. Please add to your classpath C:/prose/lib/prose-compile-loc.jar to compile succesfuly your ProseStart project.

package pl.kuligowski.prose.startup;
 
import java.io.IOException;
 
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import ch.ethz.prose.ProseSystem;
import ch.ethz.prose.SystemStartupException;
 
public class ProseStart extends HttpServlet implements Servlet {
 
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
        
        if (!ProseSystem.isActive()) {
            response.getOutputStream().write("Starting prose ...".getBytes());
            try {
                // starting PROSE system
                ProseSystem.startup();
            } catch (SystemStartupException e) {
                e.printStackTrace();
            }
        } else {
            response.getOutputStream().write("Prose started ...".getBytes());
        }
    }
}

Inside of war file containing above servlet you should place WEB-INF/web.xml describing servlet mapping.

<?xml version="1.0" encoding="UTF-8"?>
<web-app>
  <display-name>ProseAspects</display-name>
  <servlet>
    <display-name>ProseStart</display-name>
    <servlet-name>ProseStart</servlet-name>
    <servlet-class>pl.kuligowski.prose.startup.ProseStart</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ProseStart</servlet-name>
    <url-pattern>/ProseStart</url-pattern>
  </servlet-mapping>
</web-app>

After starting Apache Tomcat web server deploy ProseStart simple web application by copying created war file to C:/apache-tomcat-6.0.14/webapps directory. Open your web browser and go to url: http://localhost:8080/prose/ProseStart. You should see following output which means that PROSE system is enabled and ready to weave any aspects.

Starting prose ...

Case 1: Statistics of word requests

It is time to satisfy your boss and write aspects and weave them at runtime to your web application (AdvWeb).

The first thing we should do is define the point-cut of our WordStatsAspect - the place where written aspect should be applied. We should inject aspect before method invocation were requested word is passed as a parameter. The right place to do it is getAdvertByWord(String requestedWord) method from AdvRepository class.

The second thing is to write body of our aspect. After fetching requested word from arguments available in joint-point we increase counter and write requested word with its count on the console output.

Writing aspect printing statistics

package pl.kuligowski.prose.aspects;
 
import java.util.HashMap;
 
import ch.ethz.prose.DefaultAspect;
import ch.ethz.prose.crosscut.ANY;
import ch.ethz.prose.crosscut.Crosscut;
import ch.ethz.prose.crosscut.MethodCut;
import ch.ethz.prose.filter.Executions;
import ch.ethz.prose.filter.PointCutter;
import ch.ethz.prose.filter.Within;
 
public class WordStatsAspect extends DefaultAspect {
    
    private static final String aspectID = "WordStatsAscpet: ";
    
    private static HashMap<String, Integer> stats = new HashMap<String, Integer>();
    
    public Crosscut getAdvertByWordCrosscut = new MethodCut() {
        
        // execute advice on executions of the form *.*(String)
        public void METHOD_ARGS(ANY x, String word) {
            if (word!=null) {
                Integer wordCount = stats.get(word);
                if (wordCount==null) {
                    wordCount = new Integer(0);
                }
                wordCount++;
                stats.put(word, wordCount);
                System.out.println(aspectID + "word: " + word + "; count: " + wordCount);
            } else {
                System.out.println(aspectID + "undefined word");
            }
        }
        
        // calls(AdvRepository.getAdvertByWord(..))
        protected PointCutter pointCutter() {
            System.out.println(aspectID + "Registering.");
            return
                Executions.before().
                AND(Within.type("AdvRepository")).
                AND(Within.method("getAdvertByWord"));
        }
    };
}

Deploying

Compile your first PROSE aspect putting in project classpath C:/prose/lib/prose-compile-loc.jar file. Move all output *.class files to C:/prose/bin/aspects directory (if aspects directory does not exist create it). In our example you should have following structure of files in C:/prose/bin:

c:/prose/bin/
|--aspects
|  |--pl
|     |--kuligowski
|        |--prose
|           |--aspects
|              |--WordStatsAspect$1.class
|              |--WordStatsAspect.class
|--clprose
|--clprose.exe
|--jprose
|--jprose.exe
|--jproseHS
|--jproseHS.exe
|--wbprose
|--wbprose.exe

Weave newly created aspect into your running application by typing in command-line console window (being in C:/prose/bin directory) following command (in one line):

c:/prose/bin/>clprose -classpath aspects 
                      --insert pl.kuligowski.prose.aspects.WordStatsAspect 
                      --address localhost:5000

Where -classpath parameter points to your aspects repository *.class files, --insert command weaves into application defined aspect and --address command points RMI PROSE server location which we has started earlier. After performing above command switch to the running Apache Tomcat output console and check if following line has appeared on the console window:

2008-01-06 22:44:24 org.apache.catalina.startup.Catalina start
INFO: Server startup in 2095 ms
WordStatsAscpet: Registering.

Go to the web browser and test your AdvWeb application by refreshing AdvController servlet (http://localhost:8080/advweb/AdvController?word=gift) and changing words in url. After few tests take a look on the output console of Apache Tomcat. You should see with satisfaction that your WordStatsAspect works.

2008-01-06 22:44:24 org.apache.catalina.startup.Catalina start
INFO: Server startup in 2095 ms
WordStatsAscpet: Registering.
WordStatsAscpet: word: gift; count: 1
WordStatsAscpet: word: gift; count: 2
WordStatsAscpet: word: gift; count: 3
WordStatsAscpet: word: buy; count: 1
WordStatsAscpet: word: buy; count: 2

Congartualtions! You have woven your first aspect dynamically and remotely into running web application deployed on Apache Tomcat 6 web server. But we have more work to do, don't we?

Case 2: Changing visualisation of advertisements

The second case is real challange. We have bussiness requirement which concerns http content changes - bold and colorize requested word if found in content of advertisement and return new content to the requesting client.

At first we create aspect which is responsible for saving requested words in ThreadLocal object. Each servlet request creates new thread which has a local storage holding ThreadLocal objects. These objects are available only for working thread and not for others. So we can simply save at the begining requested word and use it later.

Writing aspect saving last requested word

The point-cut of SaveWordAspect is the same as in WordStatsAspect because we want to save requested word. So we should inject SaveWordAspect before getAdvertByWord(String requestedWord) method invocation from AdvRepository class.

We define public and static ThreadLocal variable called lastWord which holds last requested word by the client. This variable could be accessed by second aspect which is responsible for content changes. Note that solution is thread-safe because resources of ThreadLocal object are accessed only by the same thread.

package pl.kuligowski.prose.aspects;
 
import ch.ethz.prose.DefaultAspect;
import ch.ethz.prose.crosscut.ANY;
import ch.ethz.prose.crosscut.Crosscut;
import ch.ethz.prose.crosscut.MethodCut;
import ch.ethz.prose.filter.Executions;
import ch.ethz.prose.filter.PointCutter;
import ch.ethz.prose.filter.Within;
 
public class SaveWordAspect extends DefaultAspect {
 
    private static final String aspectID = "SaveWordAscpet: ";
    
    /**
     * ThreadLocal variable is holding remembered word associated with
     * current thread. This public variable will be used in future
     * to get last saved word.
     */
    public static ThreadLocal lastWord = new ThreadLocal();  
    
    public Crosscut getAdvertByWordCrosscut = new MethodCut() {
        
        // execute advice on executions of the form *.*(String)
        public void METHOD_ARGS(ANY x, String word) {
            System.out.println(aspectID + "Saving word: " + word);
            lastWord.set(word);
        }
        
        // calls(AdvRepository.getAdvertByWord(..))
        protected PointCutter pointCutter() {
            System.out.println(aspectID + "Registering.");
            return
                Executions.before().
                AND(Within.type("AdvRepository")).
                AND(Within.method("getAdvertByWord"));
        }
    };
}

Deploying

Deploy SaveWordAspect in the same way as WordStatsAspect and refresh url with AdvController servlet several times. You should see following lines on the output console from your Apache Tomcat web server which means that both of woven aspects work inside your application.

SaveWordAscpet: Registering.
SaveWordAscpet: Saving word: gift
WordStatsAscpet: word: gift; count: 4
SaveWordAscpet: Saving word: buy
WordStatsAscpet: word: buy; count: 4

Writing aspect changing advertisement content

What we have now is a saved requested word in working thread so we can find the place where view of advertisement is applied to the content and change the content in the way as our boss wants.

To be sure that our content will be returned in the same way in future we capture all read accesses to the content field in AdvView class and change returned value with changed value of advertisement. We can achieve this by defining the point-cut on field read-access (GetCut in PROSE).

When defined join-point is reached we take saved last word and replace it in advertisement content with wrapped value. After all we set new value of fetched field content and return to the requester. In AdvWeb example the requester of content field is getBytes method from AdvView class where content is concatenated with header and bottom fields and returned in byte array representation.

package pl.kuligowski.prose.aspects;
 
import ch.ethz.jvmai.FieldAccessJoinPoint;
import ch.ethz.prose.DefaultAspect;
import ch.ethz.prose.crosscut.ANY;
import ch.ethz.prose.crosscut.Crosscut;
import ch.ethz.prose.crosscut.GetCut;
import ch.ethz.prose.filter.Fields;
import ch.ethz.prose.filter.PointCutter;
 
public class BoldWordAspect extends DefaultAspect {
    
    private static final String aspectID = "BoldWordAscpet: ";
    
    public Crosscut getContentFieldCrosscut = new GetCut() {
        
        // obligatory to be defined
        public void GET_ARGS(ANY x, String arg) { }
        
        /**
         * This joint-point is reached when AdvView accesses 'content'
         * field during construction final byte array representation of view. 
         */
        public void joinPointReached(FieldAccessJoinPoint jp)
                throws Exception {
            
            String lastWord = (String) SaveWordAspect.lastWord.get();
            System.out.println(aspectID + "Receiving last saved word: " + lastWord);
            String advert = (String) jp.getValue();
            advert = advert.replaceAll(lastWord, 
                    "<span style=\"color:red; font-weight:bold; " +
                    "font-size: 14px; display:inline;\">" + 
                    lastWord + "</span>");
            jp.setValue(advert);
            System.out.println(aspectID + "Setting new content: " + advert);
            super.joinPointReached(jp);
        }
        
        // when field named 'content' declared in AdvView class is accessed
        protected PointCutter pointCutter() {
            System.out.println(aspectID + "Registering.");
            return Fields.named("content").
                AND(Fields.declaredInClass("AdvView"));
        }
    };
}

Deploying

After deploying the BoldWordAspect and refresing AdvController servlet site you should see desired effect: every requested word if it is found in advertisement content is bold and red.

Content changed by wooven aspect

Download ready ProseAspects project

ProseAspects eclipse project contains ProseStart servlet which enables PROSE system and all three written aspects. Moreover you find ant scripts which compile and deploy written source code.

Pay attention to build.properties files where you have to configure your home directory of PROSE and Apache Tomcat web server. After importing ProseAspects project into Eclipse you don't have to configure libraries because it is done and needed libraries are enclosed in project distribution.

Sources (Eclipse-ready): prose-aspects-src-1.0.zip

JBoss with PROSE?

I'm sure you wonder if PROSE system could be applied to JBoss application server. Yes, you can use dynamic and remote aspect weaving also with any kind of application servers which run in Java Runtime Environment. Replace only original Sun JRE with extended one by PROSE. Other procedures are the same.

Conclusions

AOP approach allows to separate domains of your enterprise application from each other (transactions, logging, db management, security and more) and apply every in defined places (point-cuts) in your application without touching your bussiness code. Dynamic aspect weaving opens possibility of choosing these places at runtime. You can decide during application execution that some aspect should be inserted before some method because you need it in some specific conditions.

But we want more - we want to modify our application behaviour remotely, without restarting and recompilation, without having access to the deployment directory structure. We want to apply written aspects from different machine using Internet connection. It is possible using PROSE system which allows to send aspects using RMI communication mechanism.

It is hard to decide if desribed techniques will be common in future. There is a lot of questions which in my opinion should be answered before production use:

  • What about stability and performance?
  • How to manage all woven aspects? After some time application could have for example 100 or more woven aspects and we could not exactly now what for and in what sequence we have woven all of them.
  • Is this approach secure?
  • What if our enterprise application had been restarted? Would we able to apply in short time all aspects?

I'm sure above questions are only an introduction to quite long discution about production use of remote and dynamic aspect weaving. I think the idea of remote and dynamic aspect weaving is interesting and should be taken into consideration.

3 comments to "AOP in Java Enterprise Applications"

Page generated in 0.006s

Sebastian Kuligowski's Home Page

Copyright © Krak√≥w, 2008 All Rights Reserved