/*
* 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.jmx.adaptor.html;
import java.io.IOException;
import java.net.URLDecoder;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import javax.management.AttributeList;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.jboss.jmx.adaptor.control.OpResultInfo;
import org.jboss.jmx.adaptor.control.Server;
import org.jboss.jmx.adaptor.model.MBeanData;
import org.jboss.logging.Logger;
/** The HTML adaptor controller servlet.
*
* @author Scott.Stark@jboss.org
* @version $Revision: 81662 $
*/
public class HtmlAdaptorServlet extends HttpServlet
{
private static Logger log = Logger.getLogger(HtmlAdaptorServlet.class);
private static final String ACTION_PARAM = "action";
private static final String FILTER_PARAM = "filter";
private static final String DISPLAY_MBEANS_ACTION = "displayMBeans";
private static final String INSPECT_MBEAN_ACTION = "inspectMBean";
private static final String UPDATE_ATTRIBUTES_ACTION = "updateAttributes";
private static final String INVOKE_OP_ACTION = "invokeOp";
private static final String INVOKE_OP_BY_NAME_ACTION = "invokeOpByName";
/** Creates a new instance of HtmlAdaptor */
public HtmlAdaptorServlet()
{
}
public void init(ServletConfig config) throws ServletException
{
super.init(config);
}
public void destroy()
{
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
processRequest(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
processRequest(request, response);
}
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String action = request.getParameter(ACTION_PARAM);
if( action == null )
action = DISPLAY_MBEANS_ACTION;
if( action.equals(DISPLAY_MBEANS_ACTION) )
displayMBeans(request, response);
else if( action.equals(INSPECT_MBEAN_ACTION) )
inspectMBean(request, response);
else if( action.equals(UPDATE_ATTRIBUTES_ACTION) )
updateAttributes(request, response);
else if( action.equals(INVOKE_OP_ACTION) )
invokeOp(request, response);
else if( action.equals(INVOKE_OP_BY_NAME_ACTION) )
invokeOpByName(request, response);
}
/**
* Display all mbeans categorized by domain
*/
private void displayMBeans(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException
{
// get ObjectName filter from request or session context
HttpSession session = request.getSession(false);
String filter = request.getParameter(FILTER_PARAM);
if (filter == null && session != null)
{
// try using previously provided filter from session context
filter = (String) session.getAttribute(FILTER_PARAM);
}
if (filter != null && filter.length() > 0)
{
// Strip any enclosing quotes
if (filter.charAt(0) == '"')
filter = filter.substring(1);
if (filter.charAt(filter.length() - 1) == '"')
filter = filter.substring(0, filter.length() - 2);
// be a litte it tolerant to user input
String domain = "*";
String props = "*,*";
int separator = filter.indexOf(':');
int assignment = filter.indexOf('=');
if (separator == -1 && assignment != -1)
{
// assume properties only
props = filter.trim();
}
else if (separator == -1 && assignment == -1)
{
// assume domain name only
domain = filter.trim();
}
else
{
// domain and properties
domain = filter.substring(0, separator).trim();
props = filter.substring(separator + 1).trim();
}
if (domain.equals(""))
domain = "*";
if (props.equals(""))
props = "*,*";
if (props.endsWith(","))
props += "*";
if (!props.endsWith(",*"))
props += ",*";
if (props.equals("*,*"))
props = "*";
filter = domain + ":" + props;;
if (filter.equals("*:*"))
filter = "";
}
else
{
filter = "";
}
//Change "<" and ">" to "<" and ">" in filter string
filter = translateMetaCharacters(filter);
// update request filter and store filter in session context,
// so it can be used when no filter has been submitted in
// current request
request.setAttribute(FILTER_PARAM, filter);
if (session != null)
{
session.setAttribute(FILTER_PARAM, filter);
}
Iterator mbeans;
try
{
mbeans = getDomainData(filter);
}
catch (Exception e)
{
request.setAttribute("filterError", e.getMessage());
try
{
mbeans = getDomainData("");
}
catch (Exception e1)
{
throw new ServletException("Failed to get MBeans", e);
}
}
request.setAttribute("mbeans", mbeans);
RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/displayMBeans.jsp");
rd.forward(request, response);
}
/**
* Display an mbeans attributes and operations
*/
private void inspectMBean(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String name = request.getParameter("name");
log.trace("inspectMBean, name="+name);
try
{
MBeanData data = getMBeanData(name);
request.setAttribute("mbeanData", data);
RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/inspectMBean.jsp");
rd.forward(request, response);
}
catch(Exception e)
{
throw new ServletException("Failed to get MBean data", e);
}
}
/** Update the writable attributes of an mbean
*/
private void updateAttributes(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
final String name = request.getParameter("name");
log.trace("updateAttributes, name="+name);
Enumeration paramNames = request.getParameterNames();
final HashMap attributes = new HashMap();
while( paramNames.hasMoreElements() )
{
String param = (String) paramNames.nextElement();
if( param.equals("name") || param.equals("action") )
continue;
String value = request.getParameter(param);
log.trace("name="+param+", value='"+value+"'");
// Ignore null values, these are empty write-only fields
if( value == null || value.length() == 0 )
continue;
attributes.put(param, value);
}
try
{
AttributeList newAttributes = setAttributes(name, attributes);
MBeanData data = getMBeanData(name);
request.setAttribute("mbeanData", data);
RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/inspectMBean.jsp");
rd.forward(request, response);
}
catch(Exception e)
{
throw new ServletException("Failed to update attributes", e);
}
}
/** Invoke an mbean operation given the index into the MBeanOperationInfo{}
array of the mbean.
*/
private void invokeOp(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String reqname = request.getParameter("name");
final String name = URLDecoder.decode(reqname, "UTF-8");
log.trace("invokeOp, name="+name);
final String[] args = getArgs(request);
String methodIndex = request.getParameter("methodIndex");
if( methodIndex == null || methodIndex.length() == 0 )
throw new ServletException("No methodIndex given in invokeOp form");
final int index = Integer.parseInt(methodIndex);
try
{
OpResultInfo opResult = invokeOp(name, index, args);
request.setAttribute("opResultInfo", opResult);
RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/displayOpResult.jsp");
rd.forward(request, response);
}
catch(Exception e)
{
throw new ServletException("Failed to invoke operation", e);
}
}
/** Invoke an mbean operation given the method name and its signature.
*/
private void invokeOpByName(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
final String name = request.getParameter("name");
log.trace("invokeOpByName, name="+name);
final String[] argTypes = request.getParameterValues("argType");
final String[] args = getArgs(request);
final String methodName = request.getParameter("methodName");
if( methodName == null )
throw new ServletException("No methodName given in invokeOpByName form");
try
{
OpResultInfo opResult = invokeOpByName(name, methodName, argTypes, args);
request.setAttribute("opResultInfo", opResult);
RequestDispatcher rd = this.getServletContext().getRequestDispatcher("/displayOpResult.jsp");
rd.forward(request, response);
}
catch(Exception e)
{
throw new ServletException("Failed to invoke operation", e);
}
}
/** Extract the argN values from the request into a String[]
*/
private String[] getArgs(HttpServletRequest request)
{
ArrayList argList = new ArrayList();
for(int i = 0; true ; i++)
{
String name = "arg" + i;
String value = request.getParameter(name);
if( value == null )
break;
argList.add(value);
log.trace(name+"="+value);
}
String[] args = new String[argList.size()];
argList.toArray(args);
return args;
}
/** Translate html metacharacters in filter string only '<' and '>'
*/
private String translateMetaCharacters(String s)
{
s = s.replaceAll("<","<");
s = s.replaceAll(">",">");
return s;
}
private MBeanData getMBeanData(final String name) throws PrivilegedActionException
{
return AccessController.doPrivileged(new PrivilegedExceptionAction<MBeanData>()
{
public MBeanData run() throws Exception
{
return Server.getMBeanData(name);
}
});
}
@SuppressWarnings("unchecked")
private Iterator getDomainData(final String filter) throws PrivilegedActionException
{
return AccessController.doPrivileged(new PrivilegedExceptionAction<Iterator>()
{
public Iterator run() throws Exception
{
return Server.getDomainData(filter);
}
});
}
private OpResultInfo invokeOp(final String name, final int index, final String[] args) throws PrivilegedActionException
{
return AccessController.doPrivileged(new PrivilegedExceptionAction<OpResultInfo>()
{
public OpResultInfo run() throws Exception
{
return Server.invokeOp(name, index, args);
}
});
}
private OpResultInfo invokeOpByName(final String name, final String methodName,final String[] argTypes, final String[] args) throws PrivilegedActionException
{
return AccessController.doPrivileged(new PrivilegedExceptionAction<OpResultInfo>()
{
public OpResultInfo run() throws Exception
{
return Server.invokeOpByName(name, methodName, argTypes, args);
}
});
}
@SuppressWarnings({"unchecked"})
private AttributeList setAttributes(final String name, final HashMap attributes) throws PrivilegedActionException
{
return AccessController.doPrivileged(new PrivilegedExceptionAction<AttributeList>()
{
public AttributeList run() throws Exception
{
return Server.setAttributes(name, attributes);
}
});
}
}