Introduction
beSee architecture consists of several parts which are presented here.
You can read this document to understand how beSee enhances your application.
Dedicated class loadingbeSee runs with JDK 1.3 (as WebLogic Server do), and uses two third party API:
In this part you will learn how beSee uses state of the art java (dynamic proxy, reflection) to enhance your applications on demand without interfering with the versions of software you are already using. Why an advanced class loading mechanism is needed ?
As beSee (besee.jar) is in the application classpath (J2EE application server or J2SE application),
the third party API needed by beSee like log4j should - standard assumption - be loaded by the
main
class loader.
How beSee works ?
beSee has a special directory (
see
besee.runtime
configuration directive
) where a beSee dedicated special class
loader looks for classes. This class loader is visible only from beSee and class loaded this way are not
directly
visible from
your application.
At runtime, when a class (beSee internal or insrumented class from your application) calls com.gnilux.besee.log.Logger log = com.gnilux.besee.log.LoggerFactory.getLogger("besee.struts"); ... log.debug("the method yy took xx seconds to complete"); The same mechanism (but this time using simple java reflection) is used for regular expression isolation. Want to know more ?Just remind that beSee will never interferes with your application and your own running environment, but will still provides you with an efficient way to instrument them using rich API like Log4J. The only jar needed at the application server classpath level are besee.jar, javassist.jar (in fact only a sub part is needed) and jadvise.jar if you are using Aspect Oriented enhancements (read more in User guide part). Further reading: Class PreProcessor
A class preprocessor is a java component which
preprocess
java classed just before they are loaded. It can add them lot of on-demand
features.
The schematic API of a preprocessor is as simple as follow: (source copyrighted from WebLogic) public interface ClassPreProcessor { public abstract void initialize(Hashtable hashtable); public abstract byte[] preProcess(String className, byte almostReadyBytecode[]); }
BEA made Class PreProcessor usage possible by providing a small hook in their class loading mechanism.
Using a low level API for bytecode modification, the preprocessor enhances the class without impacting the class contract (name, interfaces, method signatures, packages, inheritance...). beSee uses Javassist API (official site) , but BCel (official site) is a famous similar one (used by commercial concurent product like Precise ). Chained PreProcessor
Having a PreProcessor is great, but beSee provides an easy way to isolate runtime instrumentation thru
Chained
PreProcessor.
A Chained PreProcessor is simply a PreProcessor which is part of a chain.
Such an architecture makes it easy to provide specific PreProcessor enhancing a specific point in a class.
All is then done in configuration: you can decide to add a "method response time point of mesure" in all the
public methods of all your stateless EJB of the package com.yourcompany and have the EJB com.yourcompany.SpecificBean
increments a JMX counter during the method createNewUser(...) by using two PreProcessors, each class beeing instrumented according
to the configured chain.
besee.first=A besee.A = com.gnilux.besee.preprocessor.AllMethodsPreProcessor besee.A.next = B besee.A.classMatch = com.yourcompany..*Bean besee.A.classNotMatch = com.yourcompany.excludedpackage.* besee.B.mt.after = com.gnilux.besee.log.LoggerFactory.getLogger("besee.sample").info(...); besee.B = com.gnilux.besee.preprocessor.AllMethodsPreProcessor besee.B.next = besee.B.classMatch = com.yourcompany.SpecificBean besee.B.methodMatch = createNewUser besee.B.mt.before = com.gnilux.besee.jmx.JMXService.get("besee.sample").increment("$$method"); (configuration sample - illustration purpose) besee.class.A = com.yourcompany..*Bean besee.class.A.chain = A, B besee.class.A.A.methodMatch = .* besee.class.A.B.methodMatch = createNewUser besee.A = com.gnilux.besee.preprocessor.AllMethodsPreProcessor besee.B.mt.after = com.gnilux.besee.log.LoggerFactory.getLogger("besee.sample").info(...); besee.B = com.gnilux.besee.preprocessor.AllMethodsPreProcessor besee.B.mt.before = com.gnilux.besee.jmx.JMXService.get("besee.sample").increment("$$method"); For specific needs, it is also possible to write a custom Chained PreProcessor. Lasy JMX registration
beSee registers the PreProcessor chain as a JMX component to allow for runtime monitoring of beSee itself and hot configuration. It allows preprocessor configuration change
without restarting the app server.
To solve this issue, beSee has a lazy registration mechanism which allows to have JMX registration occur in background, waiting the JMX server part to be ready. This problem does not occurs when using beSee in J2SE mode. Flow depth supportWhen you track the execution flow of an application, it is important to keep trace of the flow depth. For example if the method Sample.main() calls Foo.newInstance(), the newInstance() method is deeper in the execution flow - as the developper probably wrote it in its source code: public class Sample { public static void main(String args[]) { foo = Foo.newInstance(args[0]); foo.do(); } } Sample#main(["hello", "guten tag", "bonjour"]) Foo#newInstance("hello") = foo[value="hello"] Foo.do() Sample#main(["hello", "guten tag", "bonjour"]) Foo#newInstance("hello") = foo[value="hello"] Foo.do() |