Pages

Tuesday, October 29, 2013

Apache Camel JMX with remote jconsole using host : port

By default when JMX is enabled in apache camel, it binds such that you will have to use a rather encrypted path in jconsole URI for connecting remotely. From there documentation-
service:jmx:rmi://localhost:<connectorPort>/jndi/rmi://localhost:<registryPort>/<serviceUrlPath>
or
service:jmx:rmi://localhost:2000/jndi/rmi://localhost:1099/jmxrmi/camel


You will not be able to connect using localhost:1099 in the jconsole. This was very important to me as I was not just using jconsole but zabbix to gather data from JMX. In case of zabbix the only option that is available was host:port. The reason this does not work is the serviceUrlPath which by default in camel is customized to /jmxrmi/camel instead of the more known /jmxrmi. The fix is easy as show below-

<camelContext id="camelAppContext" xmlns="http://camel.apache.org/schema/spring">
<propertyPlaceholder id="properties"
location="classpath:common.properties"/>
<jmxAgent id="camelAppJmxAgent" registryPort="{{jmx.registry.port}}" connectorPort="{{jmx.connector.port}}"
createConnector="true" serviceUrlPath="/jmxrmi"/>
</camelContext>

Or by using the JMX from –D options.
-Dorg.apache.camel.jmx.serviceUrlPath=/jmxrmi

With this configuration you can connect using host:port (localhost:1099) to JMX using jconsole.

Monday, October 21, 2013

Liftweb 2.4 and Jamon 2.74 request monitoring

I like Jamon as a library for collecting performance statistics. It is well established and provides sufficient types of statistics. One of the favorites statistics of mine being the frequency distribution of response times. Using Jamon in simple but with Lift 2.4 it was little challenging. The Jamon MonitorFactory returns an instance of Timing Monitor that maintains the statistics. For the collection to occur the same instance must be accessed to call stop after method execution. i.e-
Monitor monitor = MonitorFactory.start("key1");
//Code whose performance needs to be measured
//
//
monitor.stop();

The problem with older version of Liftweb is there is no way of wrapping every request such that this monitor can be used. The S.addAround can be used with LoanWrapper but this gets executed multiple times as described here. As from the discussion from that thread you can see that a common LoanWrapper surrounding all requests would be (or has already been) added in Liftweb. But, if you can not upgrade the Liftweb to newer version then here is the solution. The idea is to use the LiftRules onBeginServicing and onEndServicing. But note that we need to use the same instance of Monitor created in onBeginServicing method. The request/response lifecycle in liftweb is described very well here. This will catch both Statefull GET and Ajax requests. The code below uses the setAttribute method of the original HHTPServletRequest to save and retrieve the Monitor between methods. Have fun with scala, liftweb and jamon!
//Boot.scala
def boot {
val labelPrefix: String = "LIFT_WEB_HTTP."
val monitorInReq: String = "monitorInReq"
val extensionsNotToMonitor = List(".jpg", ".png", ".jpeg", ".ico", ".gif", ".bmp")

def pathFilter = {
req: Req =>
req.path.wholePath.takeRight(1) match {
case x :: _ if (extensionsNotToMonitor.contains(x.substring(x.lastIndexOf(".")))) => None
case _ => Some(req.path.wholePath.mkString("/"))
}
}

LiftRules.onBeginServicing.append((req: Req) => {
val label: Option[String] = pathFilter(req)
if (label.isDefined) {
val start: Monitor = MonitorFactory.start(labelPrefix + label.get)
S.containerRequest.map(r => (r.asInstanceOf[HTTPRequestServlet]).req.setAttribute(monitorInReq, start))
}
})

LiftRules.onEndServicing.append((req, respBox) => {
S.containerRequest.map(r => {
val attribute: AnyRef = (r.asInstanceOf[HTTPRequestServlet]).req.getAttribute(monitorInReq)
if (null != attribute) {
attribute.asInstanceOf[Monitor].stop()
}
})
})
}