/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.monitor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import org.jboss.naming.NonSerializableFactory;
import org.jboss.system.ServiceMBeanSupport;
/**
* MBean implementation for providing Locking Stats for EntityBeans
*
* @author <a href="mailto:bill@jboss.org">Bill Burke</a>
* @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
* @version $Revision: 81030 $
*/
public class EntityLockMonitor extends ServiceMBeanSupport
implements EntityLockMonitorMBean
{
// Constants ----------------------------------------------------
public static final String JNDI_NAME = "EntityLockMonitor";
// Protected -----------------------------------------------------
protected HashMap monitorMap = new HashMap();
protected long contenders = 0;
protected long maxContenders = 0;
protected ArrayList times = new ArrayList();
protected long contentions = 0;
protected long totalTime = 0;
protected long sumContenders = 0;
// Constructors -------------------------------------------------
public EntityLockMonitor()
{
// empty
}
// ServiceMBeanSupport overrides ---------------------------------
protected void startService()
throws Exception
{
bind();
log.info("EntityLockMonitor started");
}
protected void stopService() {
try
{
unbind();
}
catch (Exception ignored) {}
log.info("EntityLockMonitor stopped");
}
// Attributes ----------------------------------------------------
/**
* @jmx.managed-attribute
*/
public synchronized long getAverageContenders()
{
if (contentions == 0)
{
return 0;
}
else
{
return sumContenders / contentions;
}
}
/**
* @jmx.managed-attribute
*/
public synchronized long getMaxContenders()
{
return maxContenders;
}
/**
* @jmx.managed-attribute
*/
public synchronized long getMedianWaitTime()
{
if (times.size() < 1)
{
return 0;
}
Long[] alltimes = (Long[])times.toArray(new Long[times.size()]);
long[] thetimes = new long[alltimes.length];
for (int i = 0; i < thetimes.length; i++)
{
thetimes[i] = alltimes[i].longValue();
}
Arrays.sort(thetimes);
return thetimes[thetimes.length / 2];
}
/**
* @jmx.managed-attribute
*/
public synchronized long getTotalContentions()
{
return contentions;
}
// Operations ----------------------------------------------------
/**
* @jmx.managed-operation
*/
public Set listMonitoredBeans()
{
synchronized(monitorMap)
{
return new TreeSet(monitorMap.keySet());
}
}
/**
* @jmx.managed-operation
*
* @return the LockMonitor that corresponds to the jndiName or null
*/
public LockMonitor getLockMonitor(String jndiName)
{
synchronized(monitorMap)
{
return (LockMonitor)monitorMap.get(jndiName);
}
}
/**
* @jmx.managed-operation
*/
public String printLockMonitor()
{
StringBuffer rtn = new StringBuffer();
rtn.append("<table width=\"1\" border=\"1\">");
rtn.append("<tr><td><b>EJB JNDI-NAME</b></td><td><b>Total Lock Time</b></td><td><b>Num Contentions</b></td><td><b>Time Outs</b></td><td><b>Max Contenders</b></td></tr>");
synchronized(monitorMap)
{
Iterator it = monitorMap.keySet().iterator();
while (it.hasNext())
{
rtn.append("<tr>");
String jndiName = (String)it.next();
rtn.append("<td>");
rtn.append(jndiName);
rtn.append("</td>");
LockMonitor lm = (LockMonitor)monitorMap.get(jndiName);
rtn.append("<td>");
rtn.append(("" + lm.getTotalTime()));
rtn.append("</td><td>");
rtn.append(("" + lm.getNumContentions()));
rtn.append("</td><td>");
rtn.append(("" + lm.getTimeouts()));
rtn.append("</td><td>");
rtn.append(("" + lm.getMaxContenders()));
rtn.append("</td></tr>");
}
}
rtn.append("</table>");
return rtn.toString();
}
/**
* @jmx.managed-operation
*/
public synchronized void clearMonitor()
{
contenders = 0;
maxContenders = 0;
times.clear();
contentions = 0;
totalTime = 0;
sumContenders = 0;
synchronized(monitorMap)
{
Iterator it = monitorMap.keySet().iterator();
while (it.hasNext())
{
String jndiName = (String)it.next();
LockMonitor lm = (LockMonitor)monitorMap.get(jndiName);
lm.reset();
}
}
}
// Public -------------------------------------------------------
public synchronized void incrementContenders()
{
++contenders;
++contentions;
sumContenders += contenders;
if (contenders > maxContenders)
{
maxContenders = contenders;
}
}
public synchronized void decrementContenders(long time)
{
times.add(new Long(time));
--contenders;
}
public LockMonitor getEntityLockMonitor(String jndiName)
{
LockMonitor lm = null;
synchronized(monitorMap)
{
lm = (LockMonitor)monitorMap.get(jndiName);
if (lm == null)
{
lm = new LockMonitor(this);
monitorMap.put(jndiName, lm);
}
}
return lm;
}
// Private -------------------------------------------------------
private void bind() throws NamingException
{
Context ctx = new InitialContext();
// Ah ! We aren't serializable, so we use a helper class
NonSerializableFactory.bind(JNDI_NAME, this);
// The helper class NonSerializableFactory uses address type nns, we go on to
// use the helper class to bind ourselves in JNDI
StringRefAddr addr = new StringRefAddr("nns", JNDI_NAME);
Reference ref = new Reference(EntityLockMonitor.class.getName(), addr, NonSerializableFactory.class.getName(), null);
ctx.bind(JNDI_NAME, ref);
}
private void unbind() throws NamingException
{
new InitialContext().unbind(JNDI_NAME);
NonSerializableFactory.unbind(JNDI_NAME);
}
}