Tuesday, November 2, 2010

Scala exercise 5: monitoring pattern

Introduction

This is the fifth exercise in my Scala exercises series. If you haven’t seen it before, you may want to start from the previous exercises. Below is exercise 5: monitoring pattern.Suppose that you have created a web application displaying product information to and taking order from customers. From your testing, let's say you have determined that it can at most display the product page 100 times per minute, otherwise it will get painfully slow or even crash. Now, you'd like to monitor the application in production to make sure its loading is not approaching that limit. If yes, you'll promptly allocate more computing resources to it.In order to do that, it would be great if the application would support something like SNMP and provide a variable named "display counter" for you to access. Then you could use a monitoring system like zabbix or nagios to monitor it. The good news is, there is something similar for Java: it is called JMX (Java Management eXtension).Next, you'll do it step by step. Your job is to fill in the missing code.First, create a Maven project. Use the following pom.xml file:

There is nothing special except that you'll use Spring. Next, create a Scala trait PerfCountersMBean in the com.foo package:

trait PerfCountersMBean { def getDisplayCounter(): Int}

It says that you'll have an "MBean" that has a property named displayCounter. MBean stands for managed bean, which is just a Java bean that can be inspected or called from a remote management console. Next, create a Scala class to implement the MBean in the com.foo package (fill in the code later):

...class PerfCounters extends PerfCountersMBean { ...}

In order to initialize the MBean, you'll turn it into a Spring bean (you'll do it later by annotating the PerfCounters class). Then, to register it with the JVM, you'll create a Spring bean that performs the registration. So, create a beans.xml file in the src/main/resources folder:

<?xml version="1.0" encoding="UTF-8" ?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="com.foo"/> <!-- init this exporter eagerly as nobody will ask it to export the MBeans --> <bean id="mbeansExporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false"> <property name="beans"> <map> <!-- the key is the name of the MBean, which starts with the domain name (foo.com) and then some name=value pairs.

Of course, you need to increment the counter whenever the product page is displayed. So, create a servlet which should display something as the product information and more importantly, get access to your MBean (a Spring bean) and increment the counter (you'll fill in the code later):

Run "mvn package" to build the war file. Then deploy it into Tomcat. To enable JMX in the JVM on which Tomcat runs, you need to pass some JVM arguments by setting the CATALINA_OPTS environment variable before running startup.sh (or startup.bat). On Linux:

Now, fill in all the missing code above.To verify that it is working, run Tomcat with startup.sh (or startup.bat). Use netstat to check if it is listening on port 1234 (the port you specified above). Finally, run jconsole and connect to localhost:1234. Choose the "MBeans" tab and you should see your MBean in the com.foo folder. Check the value of the DisplayCounter. Access the product page at http://localhost:8080/scala-jmx/product. Then check the value again and it should have been incremented.See the answer here.

//make it a Spring component@Component//it will be accessed by concurrent threads, so it should be an actorclass PerfCounters extends PerfCountersMBean with Actor { //this will create the Java beans getter and setter @BeanProperty var displayCounter = 0

class DisplayServlet extends HttpServlet { //look up the Spring application context created by the Spring context listener. //You can't do that in the constructor as the servlet context hasn't been set //yet. So, use the "lazy" keyword to do it on-demand. lazy val context: WebApplicationContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext())