TestNG XSL-based reports

As mentioned in a previous post, starting with the 5.6 version of TestNG, we have the ability to get an XML report (in the testng-results.xml file) that takes in consideration most of the TestNG features that are not available in the JUnit-compatible XML results. Although my spare time was fairly limited lately, I finally managed to create a project for an XSL transformer to handle these reports, in order to provide a useful and user-friendly output for TestNG, as an alternative to the classic HTML reports.

The project is currently hosted on Google Code at http://code.google.com/p/testng-xslt and the current version is 0.2. For the moment you can only get it from the SVN repository, but that will change as soon as I’ll have some time to write some decent documentation and pack it with a download zip. Some sample generated reports are available at http://cosminaru.ro/test/testng-xslt.

To get started, you can call ant test in the project base directory to see it running with a sample set of data. Your output should be generated in the test/single/output subdirectory. You can also use the build.xml file as a sample of integrating this with Ant and the output of a TestNG test run. Please take in consideration that the module is relying on lots of XSL 2.0 features. As far as I can tell, the only current implementation for that is Saxon, but you don’t have to worry about that since is bundled in the lib directory of the project.

Simple way to configure mod_jk load balancer at run-time

Load balancing is a common issue for many web-sites or web-based applications. Last couple weeks I had to play around with a Java-based web application that, among other things, needed to be balanced for high availability and failover. The most handy solution for this seemed to be Apache httpd and mod_jk. I won’t get into the details about setting up and configuring this stuff as it can be read on hundreds of other websites. What I want to show you is a simple way to get a more realistic (even if not bullet-proof) load balancing configuration out of this.
Let’s take a sample mod_jk configuration of a load balancer with 2 nodes. This configuration also includes a jkstatus worker that we use to monitor the overall status of mod_jk.

JkMount                 /test/* router
JkMount                 /jkstatus/* jkstatus
...
JkWorkerProperty worker.list=router, jkstatus

JkWorkerProperty worker.worker1.host=localhost
JkWorkerProperty worker.worker1.port=8009
JkWorkerProperty worker.worker1.type=ajp13
JkWorkerProperty worker.worker1.lbfactor=1
...
JkWorkerProperty worker.worker2.host=localhost
JkWorkerProperty worker.worker2.port=8019
JkWorkerProperty worker.worker2.type=ajp13
JkWorkerProperty worker.worker2.lbfactor=1
...
JkWorkerProperty worker.router.type=lb
JkWorkerProperty worker.router.balance_workers=worker1, worker2
...
JkWorkerProperty worker.jkstatus.type=status

As you can see, the lbfactor must be set here manually and I used 1 for both nodes to create an equally balanced cluster. The problem that we encountered is that we need to set the lbfactor at runtime based on the actual load of the Tomcat nodes. For example, we might want the load factor to indicate the CPU usage or memory allocation percentage.
Using the jkstatus worker, there is a very simple way to do this, once you obtained your number that indicates the load factor of your node. Assuming that the gateway (the machine running the Apache with mod_jk) is accessible from the machines running Tomcat, we can use wget to easily set the load factor:

wget "http://gateway_ip/jkstatus/?cmd=update&from=list&w=router&sw=worker1&wf=453"

Once you did this, your value (453) can be seen in the corresponding row for worker1 on the page located at http://gateway_ip/jkstatus/.
This might not be the most enterprisey way to do this stuff, but it’s a quick solution that can provide a pretty realistic load balancing (as long as the factor itself is calculated in a realistic manner).
You can put this stuff in a script, for example an update_lbfactor.sh on a shared NFS drive and adjust it to accept the worker and the load factor as arguments:

wget "http://gateway_ip/jkstatus/?cmd=update&from=list&w=router&sw=$1&wf=$2"

This way, the nodes will just have to call this with the appropiate arguments:

update_lbfactor.sh worker2 345

Hope you find this usefull.

The wind

Absolutely impressive…

GWT? …I’m fine, thanks.

Although I can’t say I love it, I’m fine with writing pure JavaScript code. I don’t need some smart-ass engine to generate it from my Java code. Really, I can live without a tool that forces me to sodomize a Java module to be JDK 1.4 compatible, while it sits on top of a heavy machinery that is annotation and generics oriented.
I have absolutely no problem in not integrating a tool that offers me two extreme options: “map the domain model if you want to use me” or “knock yourself out with JSON, key-value DTOs or other such approaches”. In both cases I would say no thanks, I’m fine.
I could manage to get by very well without a tool that is trying to implement Swing for the Web. Didn’t you guys already get it? It doesn’t work that way! I really don’t need to think about “why the fuck is not a label a span?”, “why doesn’t text-align work? aaa, it’s a div again” or most importantly “why the fuck do I need to write 50 lines of Java code for something that is eventually rendered in half of this quantity in HTML?”
I am definitely ok without a tool for which you can find serious docs anywhere but on the project website. I really don’t have a problem in not using a tool that has a JSNI acronym for something that is merely anything more than some commented code eventually inlined at compile time.
I thank the GWT guys for making me understand that pure JavaScript is not that evil. In my work, JavaScript (and web development in general) is the mistress with which you can play some pretty unorthodox sex games while watching lesbian porn (if you have the patience for all her moods), while Java on the other hand is the always reliable wife that you want to have kids with and live happily ever after. As long as none of them sees you on the street with the other one, you’re a happy developer.
We all know Google is bored and reinvents lot’s of wheel types just for the fun of it. I would usually be ok with this too (for the sake of competition) if it could only be done with decency and if no bisexual activities were involved. The only plus there that is related to Java is probably Bob Lee’s Guice.
Really, when I look at GWT I feel that it was written late at night by a bunch of drunk unemployed wannabe programmers or by a soon-to-be-fired Fortran developer in a company adopting Groovy, that tries to save his ass. Whenever I have that feeling I know I need to move on to something else.

I *heart* XML

There’s something that keeps bothering me and I definitely have to do something about it. The thing is that I always wanted to be able to have some kind of tool that compiles XML to Java so I won’t have to write that crappy Java code anymore. I always wanted to do something like:

<method name="doSomething" static="true" return-type="void">
  <var name="var1" type="Integer">
  <if test="$var1==4">
    <then>
      <var name="service" type="IMyService" factory="MyServiceFactory"/>
      <call method="someServiceMethod" on-object="service" params="p1, p2, p3"/>
    </then>
  </if>
</method>

Of course, this whole thing will be extended in order to support any programming language and also to support scripting like Python and such. I just don’t understand how come no one saw the potential of this.
Another cool ideea is a platform for developing user interfaces. I mean, UI designers will just have to learn XML because in two years all UI will be developed using XML statements like this:

<groupbox title="some title">
  <label text="$getTextFromDb"/>
  <button caption="OK" on-button-click="#doSomthingElse()"/>
</groupbox>

This is really cool and maybe I can also offer support for JavaScript or VBScript so it’s almost like developing webapps, except that you can also build desktop applications ;) . This of course will compete with HTML but I’m sure that it will kick ass because people are tired of HTML and XML is so much cooler. For the complete retards that just can’t live without HTML there will be support to use XSL that will be run half on the server and half on the client. After all, who the hell needs these standards that already have millions protocols, frameworks and tools to support them? Reinventing the wheel will always provide us with a better wheel, isn’t it?

One other great thing that we could do for the community is to replace the current linux shells with an XML equivalent. I am sure that people are really tired of bash and such tools so I think we really need to get some XML in there. I’m thinking about something like a shell that only accepts XML commands:

<command text="startx">
  <params>
    <param index="0" value="..."/>
    <param index="1" value="..."/>
    ..
  </params>
</content>

On the other hand, is never too late to consider an application server. Maybe something combined with all the ideas above. I mean, I’m sure that EVERYONE will love to have a platform where EVERYTHING can be done with XML. This means that ALL files are XML. No property files, no jar files, no image files (we’ll keep those in HEX format in the XML :P ), no CSS files. Basically nothing that cannot be parsed with an XML parser! No one will ever have to go through the pain of writing code anymore, except for XML code, and I think that is so cool that I’m getting a hardon.
Maybe if I get my friends together and analyze this we can create some sort of platform for PC, mobile, etc, that is completely portable. Something like Java but with an accent on XML. This means that no code will have to be developed, only pure XML. We will use XSLT to compile from XML to binary code, of course, so that we won’t have to write any code ourselves while we develop this.

Yes, I believe I’m gonna start implementing some of this stuff. People just have to understand: XML is better and is the only solution no matter what you want to do, and I also believe we are really far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far, far away from getting the best out of XML.

Aaah, I almost forgot: Fuck you, Marcus!

TestNG 5.6 is out

Yes, the 5.6 version of TestNG is finally out and contains several new features and a couple of bug fixes for both the core and IDE plugins. The complete list of these can be found here.
One of the noteworthy fresh features of 5.6 that I would like to talk about is the new XML reporter. This reporter provides additional TestNG-specific info that is not available in the JUnit-compatible report. It aims at helping automatic results reporting systems by promoting a consistent XML format, while also offering the foundation for obtaining user-friendly results.
The documentation for this feature along with a sample XML output is available here. You might have noticed that the XML is class-centric. This means, that no matter how you specify your test scenarios, you will always get the output built around the classes whose methods were the subject of unit testing.
As you might have expected, this reporter is included in the list of default listeners so you don’t have to enable it by hand. It’s output will be written in the same directory as the rest of the default reports (the TestNG output folder) in a file named testng-results.xml.
There are however a couple of settings and tweaks that can be used to fine-tune the reporter to best fit your needs, as you might have noticed in the docs. These properties are passed in a JavaBeans style as we will see later.
One of these properties that you might find useful is the fileFragmentationLevel integer. This property provides a way to control the structure of the generated XML files by allowing you to separate the files into smaller pieces if you consider that one single XML file is too big to chew at once.
For the beginning, if you have more than one suite in your tests, you can choose to have the results for each suite in a separate XML file (by setting fileFragmentationLevel to 2). This way, the main file will only reference the suite files, as you can see here:

<testng-results>
  <suite url="Suite1/testng-results.xml"/>
</testng-results>

The suite file will only contain the <suite> element with the same structure as before:

<suite name="Suite1">
  <groups>
  ...
  </groups>

  <test name="test1">
    <class name="com.test.TestOne">
    ...
    </class>
  </test>
</suite>

If you want to go even deeper with this, you can have your suite test-cases written in separate files, for which you will have to set fileFragmentationLevel to 3.
In this case, besides the previous effects, you will also get the test-cases files referenced from the suite files:

<suite name="Suite1">
  <groups>
  ...
  </groups>
  <test url="test1.xml"/>
</suite>

and as you probably guessed, test1.xml looks like this:

<test name="test1">
  <class name="com.test.TestOne">
  ...
  </class>
</test>

The next question would be “how the hell do I set fileFragmentationLevel and the other properties?”. The docs also mentions this stuff and you should read that before doing anything else. However, I can detail a little bit about the most common ways to configure this reporter.
The first thing you need to know is that in 5.6, along with the other changes, there is also a new method to inject custom report listeners providing fine-grained access to their behavior. The foundation of this is the new -reporter argument that can be passed from the command line. The docs about this are here. This will inject a reporter like -listener would, but it also allows you to set some properties on it.
In our case, if for example we would like to change fileFragmentationLevel and outputDirectory of the XML reporter, we would pass to the command line something like this:

-reporter org.testng.reporters.XMLReporter:fileFragmentationLevel=2,outputDirectory=/usr/temp/results

There is also an Ant equivalent for this type of configuration, and it can be achieved by using the inner <reporter> element, which in our case would be something like this:

<target name="test" depends="build">
  <testng ...>
    <reporter classname="org.testng.reporters.XMLReporter">
      <property name="fileFragmentationLevel" value="2"/>
      <property name="outputDirectory" value="/usr/temp/results"/>
    </reporter>
    ...
  </testng>
</target>

This kind of configuration can be used starting with TestNG 5.6 for any of your custom listeners, not just the XML reporter. There are however a couple of things here that must be considered. First of all, the types that are currently supported as reporter properties are limited to the following set: java.lang.String, int, boolean, byte, char, double, float, long, short. Besides, as you might have noticed, it might be a little tricky to pass strings containing commas as parameters, although it can be easily worked-around.
Another thing, strictly related to the XML reporter, is that the built-in reporter can only use the default settings, an issue also mentioned by the documentation. You can however inject a new instance of this reporter manually with one of the methods described above (yes, the above code will create a new instance, and it will not configure the default one). However, in order not to get into conflicts with the built-in reporter you have two options: one is to disable the default listeners and the other one is to change the output directory of the manually injected instance (they both worked for me).

Hope you find this useful.

I didn’t know that! (And honestly, I don’t give a fuck about it)

A colleague of mine sent a (somehow indirect) link to this article on the team mailing list a couple of days ago. I know the guy had good intentions but title did not and neither the article.

For those of you too lazy to click the link and read the somehow obscene article, here’s the short version with “for dummies” explanations:
The title is “Google could save the world by going black”. Great! How? It appears that if Google will opt for a black background on the pages hosting their services, instead of the current white approach, it would make CRT screens consume less energy when a user will visit these pages (not that the CRT displays are a dying specie). As a natural consequence, there would be a worldwide energy savings in a value of about $75,000 per year. That’s right, not 75 millions, not 75 billions and definitely not 75 trillions – just seventy five thousands dollars a year.

Now for most people, including me, that’s enough cash to get a very good start in your family-life/career/business/etc. But what we are talking about here is a one-year time savings worldwide. Indeed every cent counts, but from this to saying that this would save the planet it’s a very long way. I would like to know how exactly is this sum of money involved into saving the world. Are we going to buy some stuff from the hardware store to create an ozone-problem-solving machine that hasn’t been invented yet? Are we going to get lots of tissues in order to absorb the water in the growing oceans? Will someone put this cash in bank so in 5000 years, through interest, we would have enough money to allocate for researchers trying to cure cancer or AIDS? What is the exact world-saving solution that would be adopted after saving this enormous amount of money or energy?

The most appropriate question would actually be: do you guys at TheInquirer live on this planet? Do you have any idea how much would be allocated from this sum for a person in year at the current world population? Also, have you ever thought that it is possible that you guys are consuming a lot more than that in hard-disk storage and internet bandwith with articles like this that say nothing?

After all, do you honestly believe that Google really gives a shit about such an amount of money? If I were Google I would have this message posted on each of my pages: We at Google are stealing $0.0000125 from your pocket YEARLY with this crappy white background (who the fuck ever approved this shit anyway?) – just let us know if you feel offended by this and we will change the background to black. I am pretty sure that there will be a couple of guys “feeling offended” and wanting a black background, but these guys will certainly not belong to the category of people that have brains.

TreeHugger at least had the decency to say about the number of saved kilowatts, and not to correlate this number with the world saving idea. It’s not that they do not have their guilt. Oh, they certainly do. First of all I don’t understand who the fuck approves this $1 researches to be posted on their website? And by the way, they also link to this blog, so it’s not even their study. Don’t you guys have an image or some standards to protect? I mean, did you just had to pick up on Google for no reason as the guy you are quoting, or what?

I am not a Google evangelist and I am pretty much interested about alternative energy, low-pollution and stuff like that. Honestly, I even feel bad when I have to put out my cigarette in an improper place. But this is completely pointless and it looks like it’s made by young ecologist wannabies trying to make their entrance in the ecological thing in order to get laid with lots of ecological young girls on some ecological mattresses with some ecological condoms.

However, an even bigger disappointment is that many important web communities have linked to this article and most of them insisted on also quoting the world-saving crap from the title. Well, a lot of the intelligent stuff out there was invented while sitting on a toilet, guys, but that has nothing to do with looking into the toilet for articles after you’re done sitting on it in the morning.

Spring by examples – RMI

RMI is just one of the strategies that you can choose from when you want to implement remoting in your Spring application. At the moment, Spring provides support for Hessian, Burlap, RMI, Web services and an own HTTP remoting solution. All of these remoting strategies were designed so they can share a common configuration style allowing you to manage the exporting and accessing method from outside your business code, in the well known non-intrusive Spring manner. This means that Spring offers a POJO-based programming model for both your server and client, no matter which remoting solution you choose. The reference documentation regarding the remoting features of Spring is available here.

For now we will take a look at how Spring handles RMI while peeking into the code of a small demo application. Our example contains a simple delivery service exposed through a DeliveryServiceIntf interface that has two methods for handling Delivery objects. Below is some of the code for these objects.

public class Delivery implements Serializable {

    private String requesterName;
    private Date deliveryDate;

    public Delivery(String requesterName, Date deliveryDate) {
        this.requesterName = requesterName;
        this.deliveryDate = deliveryDate;
    }
    ...
}

public interface DeliveryServiceIntf {

    void newDelivery(Delivery delivery);

    List getDeliveries();

}

As you can see, the service interface does not need to extend the java.rmi.Remote in order to be used as an RMI service. Also, it’s remotely accessible methods don’t need to throw java.rmi.RemoteException. This is a Spring enhancement that applies to all it’s remoting solutions, not just to RMI. But this is no reason to get scared, because Spring will manage the invocation failure in it’s own fashion by throwing a org.springframework.remoting.RemoteAccessException. This is an unchecked exception so the client can decide if it wants to catch and handle this exception or just ignore it when it’s occurrence it’s considered fatal. This is however Spring’s recommended approach, but you can also expose traditional RMI implementations with Spring. Actually, this is an important issue that you should consider and analyze when remoting with Spring. While both traditional and Spring-managed RMI clients can access a traditional RMI server implementation exported through Spring, only a Spring client will be able to access a Spring exported RMI service that uses a POJO-based model.
Getting back to our example, we have below the implementation of our service which I tried to keep as simple as possible:

public class DeliveryServiceImpl implements DeliveryServiceIntf {

    private List deliveries = new ArrayList();

    public void newDelivery(Delivery delivery) {
        deliveries.add(delivery);
    }

    public List getDeliveries() {
        return deliveries;
    }
}

After we have everything implemented on the server side, we need to configure Spring to export the service through RMI. The following Spring configuration snippet does just that.

    <bean id="deliveryService" class="com.test.rmi.server.DeliveryServiceImpl">        
    </bean>

    <bean class="org.springframework.remoting.rmi.RmiServiceExporter">
        <property name="serviceName" value="delivery-service"/>
        <property name="service" ref="deliveryService"/>
        <property name="serviceInterface" value="com.test.rmi.common.DeliveryServiceIntf"/>
        <property name="registryPort" value="1234"/>
    </bean>

This is the point where we can choose which one of Spring’s remoting solutions we want to adopt. In our case we will use org.springframework.remoting.rmi.RmiServiceExporter which will also manage the RMI registry for us. The serviceName and registryPort properties are RMI specific settings that will constitute the information the client side needs to access our service. The URL for the service in this case will look something like rmi://hostname:1234/delivery-service. The serviceInterface property indicates the POJO whose methods will be the subject of RMI invoking and the service property references a bean implementing the business logic of the service.
At this point, having all up-and-running on the server side requires a small amount of effort and it only consists of classic Spring context initialization.

public class RmiServerTest {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("com/test/rmi/server/rmi-server-context.xml");        
    }
}

Now let’s see how does it work on the client side and how should we proceed to access the remote service. Here is actually a little bit easier because everything relies on the Spring bean configuration file, as seen below, while the rest of the code is pretty straightforward.

    <bean id="deliveryService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
        <property name="serviceUrl" value="rmi://localhost:1234/delivery-service"/>
        <property name="serviceInterface" value="com.test.rmi.common.DeliveryServiceIntf"/>
    </bean>

As you can see, we just need to define the deliveryService bean of class org.springframework.remoting.rmi.RmiProxyFactoryBean. This will indicate Spring that the deliveryService bean will actually be a proxy accessing the remote service at the URL rmi://localhost:1234/delivery-service, and that the business logic of the service will be used by the client through the methods of the DeliveryServiceIntf interface.
Accessing this bean will create a proxy that we can cast to the DeliveryServiceIntf interface and that will dispatch all the calls on this interface to the RMI service. Below is the pretty self-explanatory client-side code.

public class RmiClientTest {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("com/test/rmi/client/rmi-client-context.xml");
        DeliveryServiceIntf deliveryService = (DeliveryServiceIntf) ctx.getBean("deliveryService");
        deliveryService.newDelivery(new Delivery("John Doe", new Date()));
        ...
    }
}

Therefore, the client will not be aware of the fact that the service is running remote and even less about the fact that its method calls are marshaled through RMI. The Spring bean configuration file takes care of these details, so the client code will not be affected if we change the remoting strategy or even if we choose to run the service in-process.

The code for this demo can be found here. The first step is to run ant run-server in order to launch the RMI server and afterwards you need to run ant run-client from another console to start up the RMI client.
Hope this post can give you a basic feeling about Spring’s RMI support. Please feel free to comment on any aspect of this article.

Spring by examples – method interceptors

Spring is a great Java technology that has become a very popular application framework during the past few years. My intention is not to go through the whole concepts and architectural details of the framework, because that kind of information can be easily looked up starting at http://www.springframework.org. As the article title indicates, I intend to provide hands-on examples showing the minimal requirements to bundle certain Spring functionalities in your Java applications. So, because I will not go into the “what’s under the hood” approach unless absolutely necessary, most of the examples might require the knowledge of basic Spring concepts. Anyway, the basic idea is that you must RTFM before deciding if Spring is right for your application.

The first example is a short look at a simple method intercepting strategy. You can read all about this and the whole Spring AOP API here.The source code for this example can be found here. In the project directory run ant compile run to launch the application.

For the beginning let’s consider that we have the service MyService that that has a method doSomething() performing an operation which takes a long time to execute. Below you can see the (pretty dumb) code of this method.

public class MyService {
  public void doSomething() {
    for (int i = 1; i < 10000; i++) {
      System.out.println("i=" + i);
    }
  }
}

In order to print out the performance statistics on the method call, we must first implement the interceptor that actually calculates the execution time for this method. To do this we need to implement the org.aopalliance.intercept.MethodInterceptor interface shipped with Spring. This is actually a callback providing access to the actual call of the methods of our service. The JavaDoc for this interface is here.

public class ServiceMethodInterceptor implements MethodInterceptor {
  public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    long startTime = System.currentTimeMillis();
    Object result = methodInvocation.proceed();
    long duration = System.currentTimeMillis() - startTime;
    Method method = methodInvocation.getMethod();
    String methodName = method.getDeclaringClass().getName() + "." + method.getName();
    System.out.println("Method '" + methodName + "' took " + duration + " milliseconds to run");
    return null;
  }
}

Next we need to proxy our service in order to obtain an instance whose methods are being intercepted by our ServiceMethodInterceptor. To achieve this, all it takes is a little magic in Spring’s bean configuration file, as you can see below.

<beans>
  <bean id="myService" class="com.test.MyService">
  </bean>

  <bean id="interceptor" class="com.test.ServiceMethodInterceptor">
  </bean>

  <bean id="interceptedService" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
      <ref bean="myService"/>
    </property>
    <property name="interceptorNames">
      <list>
        <value>interceptor</value>
      </list>
    </property>
  </bean>
</beans>

The key in this XML snippet is Spring’s built-in class org.springframework.aop.framework.ProxyFactoryBean which provides the actual proxying of our service. In order to obtain the desired effect we must set the target and interceptorNames properties for this bean. The target property represents the name of the bean that we want to proxy, which in our case is the myService bean. The interceptorNames property holds a list of bean names that will be used as interceptors for the proxied bean. So, yes, you can define more than one interceptor for your bean.
As everything seems to be packed pretty nice, all we need to do now is to have our service instantiated using Spring and call itâs doSomething method.

public class Test {
  public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("com/test/applicationContext.xml");
    MyService myService = (MyService)ctx.getBean("interceptedService");
    myService.doSomething();
  }
}

So we need to look up the interceptedService bean in order to get the proxied service, but if we choose to remove the performance monitor we can simply lookup the initial myService bean.
Normally, after the method doSomething has run, you should see, as the last output line, something like this:

Method 'com.test.MyService.doSomething' took 281 milliseconds to run

Except from the MethodInterceptor Spring also offers other method interception strategies. For example you can choose to handle a method execution right before or immediately after the actual call, or when an exception is thrown during the execution of your method. The reference documentation about these types of interceptors that Spring offers is available here.

Please note that basic performance monitoring can also be achieved by using Spring’s built-in PerformanceMonitorInterceptor. We used this logic just as a sample for method intercepting, but as your intuition might tell you, this is just one of the many things you can do with this feature of Spring. For example, if you need to implement a fine-grained security module, you might choose not to allow the method call to execute if the user does not have rights on the business method. So, basically, you will have to see for yourself how you can use this functionality in your application.

I hope you find this article useful.
Have a happy coding day :) .