There were recently a lot of work invested in making MoSKito even better and publishing it to Maven Central, which, btw. is finished! However, it’s time to tell you something about the new features in 2.0.x.
Today we will be talking about Counters. Well having counters seems to be such a native idea for a monitoring application, that one could wonder, why didn’t we had them earlier. And in fact we did. It just wasn’t THAT easy to use them as now. Back in the not-so-old 1.x days (like 3 month ago), you had to build the counters yourself. It wasn’t hard to do, but still had to be done. Usually this meant that you built your own custom producer, with a lot of code and a lot of ifs and thans in your code. That’s over now!
Consider following example, you run a portal and are lucky to have paying customers. To check your different payment methods you want to count payments over them and maybe monitor it with Thresholds and Accumulators. Lets say you accept three payment methods: cc (creditcard), ec (electronic card) and paypal. All you need to do is to create a simple object:
package yourpackage;
import net.anotheria.moskito.annotation.Count;
@Count
public class PaymentCounter {
/**
* Electronic card payment (lastchrifteinzug in germany).
*/
public void ec(){}
/**
* Credit card payment.
*/
public void cc(){}
/**
* Payment via paypal.
*/
public void paypal(){}
}
Congratulations, you now do have a counter.
To use it you will simply have to initialize it somewhere in your code and use it:
PaymentCounter counter = new PaymentCounter();
//now make different payments
counter.ec();
//...
counter.cc();
//... counter.paypal();
If you want to take a look at your payment stats now, simply open your WebUI:
Of course you can use this counter for further processing and add a Threshold or Accumulator to it.
Ok, but isn’t creating a whole class for monitoring purposes only an anti pattern? Actually it isn’t, but if you are uncomfortable with it, you can use the same annotation @Count on selected methods:
@Count private void ec(){} @Count private void cc(){} @Count private void paypal(){}
The methods can be where ever in your code. As long as they are in the same class you they are combined in same producer automagically. If they are not, but you want them to be, you have to explicitly define the producer id:
@Count(producerId="MyProducerId") private void paypal(){}
If you don’t want to add that many methods, you can gently ask MoSKito to separate cases by parameters instead of method names:
@CountByParameter public void pay(String method){};
Instead of calling
counter.cc(); counter.ec();
you can call
counter.pay("cc"); counter.pay("ec");
if you like it more. You can also have multiple methods, each of them would be a separate producer:
@CountByParameter public void success(String method){}; @CountByParameter public void failure(String method){};
But is this all counter can do?
Of course not. We prepare some more counters as examples of what counters can achieve absolutely simply and OOTB. This time we provide a classical example (with producers and all the stuff):
Separating Males from Females
This can be interesting for portals. You can use the provided MaleFemaleCounter to achieve that. The following example monitors calls to different pages in a webapp and could be executed from a filter. First create a producer in init() or constructor.
OnDemandStatsProducer<MaleFemaleStats> producer = new OnDemandStatsProducer<MaleFemaleStats>("pages", "category", "subsystem", new MaleFemaleStatsFactory());
Whenever you can count a hit in doFilter and know the gender (for example by reading it from a session), simply call the producer.
//male client comes to the homepage producer.getDefaultStats().incMale(); producer.getStats("homepage").incMale();
//female client comes to the homepage producer.getDefaultStats().incFemale(); producer.getStats("homepage").incFemale();
//male client comes to the messaging page producer.getDefaultStats().incMale(); producer.getStats("messaging").incMale();
Guest – MEMBER – PREMIUM Separation.
Another popular example in payment portals is separation of traffic by member status. For example if I want to calculate CPU Time or other resources spent for different types of users, I need to know which status each user has and count them. But instead of showing you the ready-to-use object, we will show you how to
BUILD YOUR OWN COUNTER.
First you will need the counter itself. It’s pretty straight forward to build one, because the net.anotheria.moskito.core.counter.GenericCounterStats does all the hard work:
public class GuestBasicPremiumStats extends GenericCounterStats{
all the GenericCounterStats needs to know now, what are the values to count:
public GuestBasicPremiumStats(String name){ super(name, "guest", "basic", "premium"); } public GuestBasicPremiumStats(String name, Interval[] intervals){ super(name, intervals, "guest", "basic", "premium"); }
The counter is there, but we need to tell the GenericCounterStats to actually count something.
public void incGuest(){ super.inc("guest"); } public void incBasic(){ super.inc("basic"); } public void incPremium(){ super.inc("premium"); }
and from time to time we might want to check the values:
public long getGuest(String intervalName){ return get("guest", intervalName); } public long getBasic(String intervalName){ return get("basic", intervalName); } public long getPremium(String intervalName){ return get("premium", intervalName); } public long getGuest(){ return get("guest", null); } public long getBasic(){ return get("basic", null); } public long getPremium(){ return get("premium", null); }
one, final, method is needed by the infrastructure, to present the data properly in the WebUI:
@Override public String describeForWebUI() { return "GuestBasicPremium"; }
The code is also available in our svn.
To use the new Counter with OnDemandProducers (and that’s what you really want) you have to add one more class, the Factory, but this is really a three-liner:
public class GuestBasicPremiumStatsFactory implements IOnDemandStatsFactory<GuestBasicPremiumStats> { private Interval[] intervalSelection; GuestBasicPremiumStatsFactory(){ this(Constants.getDefaultIntervals()); } GuestBasicPremiumStatsFactory(Interval[] myIntervals){ intervalSelection = myIntervals; } @Override public GuestBasicPremiumStats createStatsObject(String name) { return new GuestBasicPremiumStats(name, intervalSelection); } }
Again, you can check this code out in the svn.
Finally, you might want to look at your gathered data from time to time, and that’s when a decorator comes into play. Again, the net.anotheria.moskito.webui.decorators.counter.GenericCounterDecorator does all the hard work, you simply have to provide some names.
Each decorator provides three strings for a value:
- Caption – The column name in the WebUI.
- ShortExplanation – Text shown on mouseover.
- Explanation – (long) text shown on the help page.
Knowing that building a decorator is piece of cake:
public class GuestBasicPremiumStatsDecorator extends GenericCounterDecorator{ private static final String CAPTIONS[] = { "Guest", "Basic", "Premium" }; private static final String SHORT_EXPLANATIONS[] = CAPTIONS; private static final String EXPLANATIONS[] = { "Number of calls, clicks, payments - whatever you wanted to count for guest users", "Number of calls, clicks, payments - whatever you wanted to count for basic users", "Number of calls, clicks, payments - whatever you wanted to count for premium users", }; private static final GuestBasicPremiumStats PATTERN = new GuestBasicPremiumStats("pattern"); public GuestBasicPremiumStatsDecorator(){ super(PATTERN, CAPTIONS, SHORT_EXPLANATIONS, EXPLANATIONS); } }
Voila. We are done. Now call this line somewhere and everything is pretty:
DecoratorRegistryFactory.getDecoratorRegistry().addDecorator(GuestBasicPremiumStats.class, new GuestBasicPremiumStatsDecorator());
Enough for today? 🙂
At least for me. So what did we learn today:
- That counters are easy to use, lightweight stat objects, that just count.
- Counters do not support time measure and are not present in journeys.
- Counters can be used as OnDemandProducers (MaleFemaleStats example).
- The provided one-dimensional CounterStats can be used very convenient together with OOTB @Count annotation
- The later works with AOP and CDI (or exists for both worlds).
- … and the counters are pure fun once you try them! 😉