/* * RHQ Management Platform * Copyright (C) 2005-2013 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.enterprise.communications.servlet; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import javax.management.MBeanServer; import javax.management.MBeanServerInvocationHandler; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.jboss.logging.Logger; import org.jboss.remoting.transport.servlet.ServletServerInvokerMBean; /** * Extends JBoss/Remoting 2's servlet that receives the inital http request for the ServletServerInvoker. * This extension finds remoting ServletServerInvoker using our own mechanism. We need to do this since * Remoting 2.4+ changed the ObjectName such that we can't statically define it in the web.xml. * The new ObjectName that Remoting uses is: * jboss.remoting:service=invoker,transport=servlet,host=myhost,port=7080,rhq.communications.connector.rhqtype=server * but we can't assume users won't configure RHQ with a different port and we don't know at build time * host name. */ public class ServerInvokerServlet extends org.jboss.remoting.transport.servlet.web.ServerInvokerServlet { private static final long serialVersionUID = 1L; private static Logger log = Logger.getLogger(ServerInvokerServlet.class); private ServletConfig servletConfig; private AtomicInteger evilLogMessageCount = new AtomicInteger(0); private final AtomicBoolean alreadyInitialized = new AtomicBoolean(false); @Override public void init(ServletConfig config) throws ServletException { // purposefully do not call init(config) - we can't have the superclass try to get // the servletInvoker yet and risk getting that dreaded and evil log message! super.init(); this.servletConfig = config; } @Override protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { // find the servlet invoker now - catch exceptions and avoid logging so we don't scare people if (initServletInvokerNow()) { super.processRequest(request, response); } else { // tell the client we can't process the request response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Server is not ready yet"); } } /** * Performs the "real" init so the JBoss/Remoting superclass loads the servlet invoker. * This returns true if we can keep going; false if an error occurred and we can't process the current request. */ private boolean initServletInvokerNow() { try { synchronized (alreadyInitialized) { if (!alreadyInitialized.get()) { super.init(this.servletConfig); alreadyInitialized.set(true); } } return true; } catch (Exception e) { // only log a message (at debug level) if we see this lots of times, but only every 10th time int msgCount = evilLogMessageCount.incrementAndGet(); if ((msgCount % 10 == 0) && (msgCount >= 100)) { log.debug(e); } return false; } } /** * Returns the servlet server invoker. In our implementation, this always returns * the MBean. * * @param config the servlet configuration * @return the servlet server invoker using the "invokerName" to query to find the MBean * @throws ServletException */ protected ServletServerInvokerMBean getInvokerFromInvokerName(ServletConfig config) throws ServletException { // get the invokerName initial parameter as defined in web.xml String invokerNameString = config.getInitParameter("invokerName"); if (invokerNameString == null) { throw new ServletException("RHQ's use of this Servlet requires invokerName init parameter"); } // the invoker name should be a query ObjectName that should find the 1 MBean we are looking for ObjectName invokerObjectNameQuery = null; try { invokerObjectNameQuery = new ObjectName(invokerNameString); log("invokerObjectNameQuery=" + invokerObjectNameQuery); } catch (MalformedObjectNameException e) { throw new ServletException("Failed to build invokerObjectNameQuery", e); } // Lookup the MBeanServer String mbeanServerId = config.getInitParameter("mbeanServer"); MBeanServer mbeanServer = getMBeanServer(mbeanServerId); if (mbeanServer == null) { throw new ServletException("Failed to locate the MBeanServer"); } Set<ObjectName> mbeans = mbeanServer.queryNames(invokerObjectNameQuery, null); if (mbeans.isEmpty()) { throw new ServletException( "Could not find the remoting servlet invoker [" + invokerObjectNameQuery + "]. DURING SERVER STARTUP AND INITIALIZATION THIS IS NOT AN ERROR AND CAN BE IGNORED. This may be a problem if occurring during normal server runtime."); } if (mbeans.size() > 1) { throw new ServletException("Found more than one remoting servlet invoker at [" + invokerObjectNameQuery + "]=" + mbeans); } ObjectName theInvokerObjectName = mbeans.iterator().next(); log("Found RHQ remoting servlet: " + theInvokerObjectName); return MBeanServerInvocationHandler.newProxyInstance(mbeanServer, theInvokerObjectName, ServletServerInvokerMBean.class, false); } }