/*
* Copyright 2002-2009 Greg Hinkle
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mc4j.ems.impl.jmx.connection.support.providers.proxy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mc4j.ems.connection.EmsConnectException;
import org.mc4j.ems.connection.EmsUnsupportedTypeException;
import org.mc4j.ems.connection.LoadException;
import org.mc4j.ems.connection.support.ConnectionProvider;
import org.mc4j.ems.impl.jmx.connection.support.providers.JBossConnectionProvider;
import javax.management.MBeanServer;
import java.io.NotSerializableException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.rmi.NoSuchObjectException;
/**
*
* @author Greg Hinkle (ghinkle@users.sourceforge.net), January 2002
* @author Ian Springer
* @version $Revision: 599 $($Author: ianpspringer $ / $Date: 2009-07-10 17:35:14 -0400 (Fri, 10 Jul 2009) $)
*/
public class GenericMBeanServerProxy implements InvocationHandler, StatsProxy {
private static Log log = LogFactory.getLog(GenericMBeanServerProxy.class);
private Object remoteServer;
private ConnectionProvider provider;
private long roundTrips;
private long failures;
private boolean reconnecting = false;
/** Creates a new instance of Proxy */
public GenericMBeanServerProxy(Object remoteServer) {
this.remoteServer = remoteServer;
}
public GenericMBeanServerProxy() {
}
public ConnectionProvider getProvider() {
return provider;
}
public void setProvider(ConnectionProvider provider) {
this.provider = provider;
}
public Object getRemoteServer() {
return remoteServer;
}
public void setRemoteServer(Object remoteServer) {
this.remoteServer = remoteServer;
}
public Object invoke(
Object proxy, Method m, Object[] args)
throws Throwable {
// SwingUtility.eventThreadAlert();
//
// ConnectionInfoAction.addHit();
Class serverClass = this.remoteServer.getClass();
//org.openide.windows.IOProvider.getDefault().getStdOut().println("Looking at object: " + serverClass.getName());
// TODO GH: This is horribly inefficient
Method[] ms = serverClass.getMethods();
Method queryMethod = null;
for (int i = 0; i < ms.length; i++) {
//org.openide.windows.IOProvider.getDefault().getStdOut().println("\t" + ms[i].getName() +
// Arrays.asList(ms[i].getParameterTypes()));
if (ms[i].getName().equals("queryMBeans"))
queryMethod = ms[i];
}
Method method;
if ("queryMBeans".equals(m.getName())) {
method = queryMethod;
//method = serverClass.getMethod(m.getName(), new Class[] { ObjectName.class, QueryExp.class });
} else {
method = serverClass.getMethod(m.getName(),m.getParameterTypes());
}
if (method == null) {
throw new EmsConnectException("Unsupported operation [" + m.getName() + "]");
}
ClassLoader ctxLoader = Thread.currentThread().getContextClassLoader();
try {
roundTrips++;
// Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
boolean isJBossConnection = (this.provider instanceof JBossConnectionProvider);
if (isJBossConnection) {
JBossConnectionProvider jbossProvider = (JBossConnectionProvider) this.provider;
// See https://jira.jboss.org/jira/browse/JOPR-9 for an explanation of why we have to re-set the
// PrincipalInfo prior to every JBoss JMX call.
//jbossProvider.resetPrincipalInfo();
// Login via JAAS before making the call...
jbossProvider.login();
}
Object returnValue;
try {
returnValue = method.invoke(this.remoteServer, args);
} finally {
if (isJBossConnection) {
JBossConnectionProvider jbossProvider = (JBossConnectionProvider) this.provider;
// Logout via JAAS before returning...
jbossProvider.logout();
}
}
return returnValue;
} catch(InvocationTargetException e) {
failures++;
if (e.getCause() != null) {
Throwable t = e.getCause();
if (t instanceof java.rmi.ConnectException) {
throw new EmsConnectException(t);
} else if (t instanceof NoSuchObjectException) {
// This happens when the server comes back up and the stub is stale
// try to reconnect if this provider supports it (if it told the proxy what the provider was)
if (provider != null && !reconnecting) {
try {
log.info("Reestablishing RMI stub " + this.provider.getConnectionSettings().getServerUrl());
reconnecting = true;
provider.connect();
// Retry the request once
return this.invoke(proxy, m, args);
} catch(Exception f) {
log.warn("Unable to reestablish RMI stub to restarted server.",f);
} finally{ reconnecting = false; }
}
// The reconnect failed, throw the original exception
// If the retry fails, it will throw its own exception
throw new EmsConnectException(t);
} else if (t instanceof java.io.IOException) {
throw new EmsConnectException(t);
} else if (t instanceof NotSerializableException) {
throw new EmsUnsupportedTypeException("Value was not serializable " + t.getLocalizedMessage(),t);
} else {
throw new EmsConnectException("Connection failure " + t.getLocalizedMessage(), t);
}
} else {
throw e;
}
} catch (Exception e) {
failures++;
//e.printStackTrace();
throw e;
} finally {
// Thread.currentThread().setContextClassLoader(ctxLoader);
}
}
public long getRoundTrips() {
return roundTrips;
}
public long getFailures() {
return failures;
}
public MBeanServer buildServerProxy() {
try {
Object proxy = Proxy.newProxyInstance(
GenericMBeanServerProxy.class.getClassLoader(),
new Class<?>[] { Class.forName("javax.management.MBeanServer")},
this);
return (MBeanServer) proxy;
} catch (ClassNotFoundException e) {
throw new LoadException("Unable to find JMX Classes", e);
}
}
}