You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by da...@apache.org on 2003/11/06 20:52:50 UTC
cvs commit: incubator-geronimo/modules/kernel/src/java/org/apache/geronimo/kernel/service GeronimoMBeanEndpoint.java GeronimoMBeanEndpointConnection.java
dain 2003/11/06 11:52:50
Added: modules/kernel/src/java/org/apache/geronimo/kernel/service
GeronimoMBeanEndpoint.java
GeronimoMBeanEndpointConnection.java
Log:
Initial revision. Endpoints are container managed connections to other
mbeans in Geronimo.
Revision Changes Path
1.1 incubator-geronimo/modules/kernel/src/java/org/apache/geronimo/kernel/service/GeronimoMBeanEndpoint.java
Index: GeronimoMBeanEndpoint.java
===================================================================
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Geronimo" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache Geronimo", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* ====================================================================
*/
package org.apache.geronimo.kernel.service;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.MBeanServerNotification;
import javax.management.Notification;
import javax.management.NotificationFilterSupport;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geronimo.kernel.jmx.JMXUtil;
import org.apache.geronimo.kernel.jmx.MBeanProxyFactory;
import org.apache.geronimo.kernel.management.NotificationType;
import org.apache.geronimo.kernel.management.State;
import net.sf.cglib.reflect.FastMethod;
/**
*
*
* @version $Revision: 1.1 $ $Date: 2003/11/06 19:52:50 $
*/
public class GeronimoMBeanEndpoint implements NotificationListener, GeronimoMBeanTarget {
private static final Log log = LogFactory.getLog(GeronimoMBeanEndpoint.class);
/**
* Is this class still mutable from users.
*/
private final boolean immutable;
/**
* Is this endpoint running.
*/
private boolean running = false;
/**
* Name of this endpoint.
*/
private String name;
/**
* A user displayable descrption of this endpoint.
*/
private String description;
/**
* Type of this endpoint.
*/
private String type;
/**
* Interface this Geronimo MBean uses to refer to the other.
*/
private final Class iface;
/**
* The object name patters for object to communicate with
*/
private Collection peers;
/**
* The connection to the other mbean
*/
private Map connections;
/**
* The proxies for the connections
*/
private Map proxies;
/**
* Is this endpoint single valued or multi (collection) valued.
*/
private final boolean singleValued;
/**
* Is this endpoint required. That means the bean can not start until there
* is a match for the endpoint. This is only valid for single valued endpoints.
*/
private boolean required = false;
/**
* Logical name of the target.
*/
private String targetName;
/**
* Name of the setter method.
* The default is "set" + name. In the case of a defualt value we do a caseless search for the name.
*/
private String setterName;
/**
* The object on which the getter and setter will be invoked
*/
private final Object target;
/**
* The method that will be called to set the attribute value. If null, the value will only be
* set into the cache.
*/
private final FastMethod setterMethod;
/**
* Context back to the Geronimo MBean that owns this endpoint
*/
private GeronimoMBeanContext context;
/**
* A reference to the dependency service.
*/
private DependencyService2MBean dependency;
public GeronimoMBeanEndpoint() {
this(null, null, Collections.EMPTY_SET, false);
}
public GeronimoMBeanEndpoint(String name, String type) {
this(name, type, Collections.EMPTY_SET, false);
}
public GeronimoMBeanEndpoint(String name, String type, ObjectName pattern) {
this(name, type, Collections.singleton(pattern), false);
}
public GeronimoMBeanEndpoint(String name, String type, ObjectName pattern, boolean required) {
this(name, type, Collections.singleton(pattern), required);
}
public GeronimoMBeanEndpoint(String name, String type, Collection peers) {
this(name, type, peers, false);
}
public GeronimoMBeanEndpoint(String name, String type, Collection peers, boolean required) {
this.name = name;
this.type = type;
this.peers = new HashSet(peers);
this.required = required;
iface = null;
target = null;
setterMethod = null;
singleValued = false;
immutable = false;
}
/**
* Creates an immutable copy of the source GeronimoAttributeInfo.
* @param source the source GeronimoAttributeInfo to copy
* @param parent the GeronimoMBeanInfo that will contain this attribute
*/
GeronimoMBeanEndpoint(GeronimoMBeanEndpoint source, GeronimoMBeanInfo parent) {
immutable = true;
//
// Required
//
// name
if (source.name == null) {
throw new IllegalArgumentException("Source must have a name");
}
name = source.name;
// peers
if (source.peers.isEmpty()) {
throw new IllegalArgumentException("Source must have at lease one peer specified");
}
peers = new HashSet(source.peers);
// interface type
if (source.type == null) {
throw new IllegalArgumentException("Source must have a type specified");
}
type = source.type;
try {
iface = Thread.currentThread().getContextClassLoader().loadClass(type);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Interface class could not be loaded: type=" + type);
}
if (!iface.isInterface()) {
throw new IllegalArgumentException("Interface class is not an interface: type=" + type);
}
// required
required = source.required;
//
// Optional
//
// setterName
if (source.setterName != null) {
setterName = source.setterName;
}
name = source.name;
description = source.description;
//
// Optional (derived)
//
// target
if (source.target != null) {
targetName = source.targetName;
target = source.target;
} else if (source.targetName == null) {
targetName = GeronimoMBeanInfo.DEFAULT_TARGET_NAME;
target = parent.getTarget();
} else {
targetName = source.targetName;
target = parent.getTarget(targetName);
if (target == null) {
throw new IllegalArgumentException("Target not found: targetName=" + targetName);
}
}
// setter proxy
Method[] methods = target.getClass().getMethods();
Method setterJavaMethod = null;
if (setterName == null) {
setterName = "set" + name;
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if (method.getParameterTypes().length == 1 && setterName.equalsIgnoreCase(method.getName())) {
setterJavaMethod = method;
setterName = method.getName();
break;
}
}
} else {
// even though we have an exact name we need to search the methods because we don't know the parameter type
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if (method.getParameterTypes().length == 1 && setterName.equals(method.getName())) {
setterJavaMethod = method;
break;
}
}
}
if (setterJavaMethod == null) {
throw new IllegalArgumentException("Setter method not found on target:" +
" setterName=" + setterName +
" targetClass=" + target.getClass().getName());
}
if (Collection.class == setterJavaMethod.getParameterTypes()[0]) {
singleValued = false;
} else if (setterJavaMethod.getParameterTypes()[0].isAssignableFrom(iface)) {
singleValued = true;
} else {
throw new IllegalArgumentException("Setter parameter must be Collection or " + type);
}
setterMethod = parent.getTargetFastClass(targetName).getMethod(setterJavaMethod);
}
public String getName() {
return name;
}
public void setName(String name) {
if (immutable) {
throw new IllegalStateException("Data is no longer mutable");
}
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
if (immutable) {
throw new IllegalStateException("Data is no longer mutable");
}
this.description = description;
}
public String getType() {
return type;
}
public void setType(String type) {
if (immutable) {
throw new IllegalStateException("Data is no longer mutable");
}
this.type = type;
}
public Collection getPeers() {
return peers;
}
public void setPeers(Collection peers) {
if (immutable) {
throw new IllegalStateException("Data is no longer mutable");
}
this.peers = new HashSet(peers);
}
public void addPeer(ObjectName peer) {
if (immutable) {
throw new IllegalStateException("Data is no longer mutable");
}
peers.add(peer);
}
public boolean isRequired() {
return required;
}
public void setRequired(boolean required) {
if (immutable) {
throw new IllegalStateException("Data is no longer mutable");
}
this.required = required;
}
public String getTargetName() {
return targetName;
}
public void setTargetName(String targetName) {
if (immutable) {
throw new IllegalStateException("Data is no longer mutable");
}
this.targetName = targetName;
}
public String getSetterName() {
return setterName;
}
public void setSetterName(String setterName) {
if (immutable) {
throw new IllegalStateException("Data is no longer mutable");
}
this.setterName = setterName;
}
public synchronized void setMBeanContext(GeronimoMBeanContext context) {
if (context != null) {
this.context = context;
MBeanServer server = context.getServer();
try {
// get a proxy to the dependency service
dependency = (DependencyService2MBean) MBeanProxyFactory.getProxy(
DependencyService2MBean.class,
server,
new ObjectName("geronimo.boot:role=DependencyService2"));
// register for mbean registration notifications
NotificationFilterSupport mbeanServerFilter = new NotificationFilterSupport();
mbeanServerFilter.enableType(MBeanServerNotification.REGISTRATION_NOTIFICATION);
mbeanServerFilter.enableType(MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
context.getServer().addNotificationListener(JMXUtil.DELEGATE_NAME, this, mbeanServerFilter, null);
} catch (JMException e) {
// this will never happen... all of the above is well formed
throw new AssertionError(e);
}
// setup the inital connections
connections = new HashMap();
proxies = new HashMap();
for (Iterator targetIterator = peers.iterator(); targetIterator.hasNext();) {
ObjectName target = (ObjectName) targetIterator.next();
Set names = server.queryNames(target, null);
for (Iterator objectNameIterator = names.iterator(); objectNameIterator.hasNext();) {
ObjectName peer = (ObjectName) objectNameIterator.next();
// if we haven't seen this one before
if (!connections.containsKey(peer)) {
// register for state change notifications
try {
server.addNotificationListener(peer, this, NotificationType.STATE_CHANGE_FILTER, null);
} catch (InstanceNotFoundException e) {
// the instance died before we could get going... not a big deal
break;
}
// if the bean is running add a connection
if (isRunning(peer)) {
addConnection(peer);
}
}
}
}
} else {
MBeanServer server = this.context.getServer();
ObjectName objectName = this.context.getObjectName();
this.context = null;
// we are ending so we need to remove all holds
if (required) {
dependency.removeStartHolds(objectName, peers);
}
// close all the connections
for (Iterator iterator = connections.values().iterator(); iterator.hasNext();) {
GeronimoMBeanEndpointConnection connection = (GeronimoMBeanEndpointConnection) iterator.next();
try {
server.removeNotificationListener(connection.getObjectName(), this);
} catch (JMException e) {
// no big deal.. just being a good citizen
log.debug("Was not a listener on a connected mbean", e);
}
if (required) {
dependency.removeDependency(objectName, connection.getObjectName());
}
connection.invalidate();
}
connections.clear();
connections = null;
if(proxies != null) {
proxies.clear();
proxies = null;
}
dependency = null;
try {
server.removeNotificationListener(JMXUtil.DELEGATE_NAME, this);
} catch (JMException ignore) {
// not important... just means the server is not valid or we were never registered
}
}
}
public synchronized boolean canStart() {
return !running || !required || connections.size() == 1;
}
public synchronized void doStart() {
if (running) {
throw new IllegalStateException("Endpoint is already running");
}
running = true;
proxies = new HashMap();
// Do we have enough connections?
if (singleValued && connections.size() != 1) {
context.fail();
return;
}
// open all the connections
for (Iterator iterator = connections.values().iterator(); iterator.hasNext();) {
GeronimoMBeanEndpointConnection connection = (GeronimoMBeanEndpointConnection) iterator.next();
connection.open();
proxies.put(connection.getObjectName(), connection.getProxy());
}
// set the collection or instance proxy into the target
if (singleValued) {
// we must block all other mbeans with matching name from starting
dependency.addStartHolds(context.getObjectName(), peers);
// set the connection into the target
GeronimoMBeanEndpointConnection connection = (GeronimoMBeanEndpointConnection) connections.values().iterator().next();
setEndpointProxy(connection.getProxy());
} else {
setEndpointProxy(Collections.unmodifiableCollection(proxies.values()));
}
}
public boolean canStop() {
return true;
}
public synchronized void doStop() {
if(!running) {
return;
}
running = false;
// set null into target
setEndpointProxy(null);
// close all the connections
for (Iterator iterator = connections.values().iterator(); iterator.hasNext();) {
GeronimoMBeanEndpointConnection connection = (GeronimoMBeanEndpointConnection) iterator.next();
connection.close();
}
proxies.clear();
proxies = null;
// we must block all other mbeans with matching name from starting
dependency.removeStartHolds(context.getObjectName(), peers);
}
public void doFail() {
doStop();
}
private synchronized void setEndpointProxy(Object proxy) {
try {
setterMethod.invoke(target, new Object[]{proxy});
} catch (RuntimeException e) {
throw e;
} catch (Error e) {
throw e;
} catch (Throwable t) {
throw new AssertionError(t);
}
}
public synchronized void handleNotification(Notification notification, Object o) {
MBeanServer server = context.getServer();
String type = notification.getType();
if (MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(type)) {
ObjectName source = ((MBeanServerNotification) notification).getMBeanName();
// if this is not a possible peer we are done
if (!isPossiblePeer(source)) {
return;
}
// register for state change notifications
try {
server.addNotificationListener(source, this, NotificationType.STATE_CHANGE_FILTER, null);
} catch (InstanceNotFoundException e) {
// the instance died before we could get going... not a big deal
return;
}
if (isRunning(source)) {
addConnection(source);
}
} else if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION.equals(type)) {
final ObjectName source = ((MBeanServerNotification) notification).getMBeanName();
if (connections.containsKey(source)) {
removeConnection(source);
}
} else if (NotificationType.STATE_RUNNING.equals(type)) {
final ObjectName source = (ObjectName) notification.getSource();
if (isPossiblePeer(source)) {
addConnection(source);
}
} else if (NotificationType.STATE_STOPPED.equals(type)) {
final ObjectName source = (ObjectName) notification.getSource();
if (connections.containsKey(source)) {
removeConnection(source);
}
} else if (NotificationType.STATE_FAILED.equals(type)) {
final ObjectName source = (ObjectName) notification.getSource();
if (connections.containsKey(source)) {
if (running && required) {
context.fail();
} else {
removeConnection(source);
}
}
}
}
/**
* Adds a connection to a peer. This does not actively open a connection, only creates one.
* If we end up with only one connectiona and are required, a dependency will be declared, and
* the component will attempt to fully start. On the other hand, if we are required and we now
* have two connections, we remove the depenency to the first we are in an ambiguous state and
* can no longer start.
*
* @param peer the name of the component to create a connection to
*/
private synchronized void addConnection(ObjectName peer) {
if (connections.containsKey(peer)) {
// we already have an connection to this peer
return;
}
// if required update the dependencies
if (required) {
if (connections.size() == 1) {
// there is now more then one possible parent so we need to remove our dependency on the current one
GeronimoMBeanEndpointConnection connection =
(GeronimoMBeanEndpointConnection) connections.values().iterator().next();
dependency.removeDependency(context.getObjectName(), connection.getObjectName());
} else if (connections.isEmpty()) {
dependency.addDependency(context.getObjectName(), peer);
}
}
// create a connection
final GeronimoMBeanEndpointConnection connection = new GeronimoMBeanEndpointConnection(iface, context.getServer(), peer);
connections.put(peer, connection);
// update running state
if (required) {
if (running) {
// this is bad... someone else started runnign that matches our hold list
context.fail();
return;
}
// if we have a single connection we are ready to start
if (connections.size() == 1) {
try {
context.start();
} catch (Exception e) {
log.warn("A problem occured while attemping to start", e);
}
}
} else if (running) {
// this is a new connection in a running endpoint, open it
connection.open();
proxies.put(connection.getObjectName(), connection.getProxy());
if(singleValued) {
setEndpointProxy(connection.getProxy());
}
}
}
/**
* Removes a connection to a component. If the connection is open it will be closed and any
* proxies to the connection will be dropped. If we are required and after the removal we have
* only a single connection, the component will attempt to fully start.
* @param peer
*/
private synchronized void removeConnection(ObjectName peer) {
if (connections.containsKey(peer)) {
// we already have an connection to this peer
return;
}
GeronimoMBeanEndpointConnection connection =
(GeronimoMBeanEndpointConnection) connections.remove(peer);
if (required) {
// Update the dependencies
dependency.removeDependency(context.getObjectName(), peer);
if (connections.size() == 1) {
GeronimoMBeanEndpointConnection parent =
(GeronimoMBeanEndpointConnection) connections.values().iterator().next();
dependency.addDependency(context.getObjectName(), parent.getObjectName());
}
if (running) {
// this is bad... mbean we were connected to stopped
context.fail();
return;
}
// if we are left with a singled connection we are ready to start
if (connections.size() == 1) {
try {
context.start();
} catch (Exception e) {
log.warn("A problem occured while attemping to start", e);
}
}
} if (running) {
// connection died... clean up
if(singleValued) {
setEndpointProxy(null);
}
proxies.remove(connection.getObjectName());
connection.close();
}
}
/**
* Is the component in the Running state
* @param objectName name of the component to check
* @return true if the component is running; false otherwise
*/
private boolean isRunning(ObjectName objectName) {
try {
final int state = ((Integer) context.getServer().getAttribute(objectName, "state")).intValue();
return state != State.RUNNING_INDEX;
} catch (AttributeNotFoundException e) {
// ok -- mbean is not a startable
return true;
} catch (InstanceNotFoundException e) {
// mbean is no longer registerd
return false;
} catch (Exception e) {
// problem getting the attribute, mbean has most likely failed
return false;
}
}
/**
* Is the component a possible peer. A component is a possible peer if
* its name matched onee of the object name patterns we watch.
* @param objectName name of the component to check
* @return true if the component is a possible peer; false otherwise
*/
private synchronized boolean isPossiblePeer(ObjectName objectName) {
for (Iterator iterator = peers.iterator(); iterator.hasNext();) {
ObjectName pattern = (ObjectName) iterator.next();
if (pattern.apply(objectName)) {
return true;
}
}
return false;
}
}
1.1 incubator-geronimo/modules/kernel/src/java/org/apache/geronimo/kernel/service/GeronimoMBeanEndpointConnection.java
Index: GeronimoMBeanEndpointConnection.java
===================================================================
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache Geronimo" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache Geronimo", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* ====================================================================
*/
package org.apache.geronimo.kernel.service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.HashMap;
import javax.management.MBeanException;
import javax.management.ReflectionException;
import javax.management.RuntimeOperationsException;
import javax.management.RuntimeMBeanException;
import javax.management.RuntimeErrorException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
/**
* This handles a connection to another mbean.
*
* @version $Revision: 1.1 $ $Date: 2003/11/06 19:52:50 $
*/
class GeronimoMBeanEndpointConnection {
/**
* Map from method proxies to mbeanInvoker.
*/
private Map methodMap = new HashMap();
/**
* The MBean server to Invoke.
*/
private MBeanServer server;
/**
* The object name to which we are connected.
*/
private ObjectName objectName;
/**
* The interface for the proxy
*/
private Class iface;
/**
* Proxy to the to this connection.
*/
private Object proxy;
/**
* The invocation handler for the proxy
*/
private ConnectionInvocationHandler invocationHandler;
/**
* Is this connection open?
*/
private boolean open = false;
/**
* Creates a new connection to the specified component using the specified interface.
*
* @param iface the interface for the proxy to the component
* @param server the mbean server in which the component is registered
* @param objectName the name of the component
*/
public GeronimoMBeanEndpointConnection(Class iface, MBeanServer server, ObjectName objectName) {
assert iface != null: "iface can not be null";
assert iface.isInterface(): "iface must be an interface";
assert server != null: "Server can not be null";
assert objectName != null: "Object name can not be null";
assert !objectName.isPattern(): "Object name can not be a pattern";
this.iface = iface;
this.server = server;
this.objectName = objectName;
Method[] methods = iface.getMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
methodMap.put(method, new MBeanInvoker(method));
}
}
/**
* Completely cleans up the internal state of the component.
*/
public synchronized void invalidate() {
open = false;
if(invocationHandler != null) {
invocationHandler.invalidate();
invocationHandler = null;
}
proxy = null;
server = null;
objectName = null;
methodMap.clear();
methodMap = null;
proxy = null;
}
/**
* Gets the proxy to the component. The proxy will implement the interface set during construction.
* The proxy is null if the connection is not open.
* @return a proxy to the componet
*/
public synchronized Object getProxy() {
return proxy;
}
/**
* Gets the object name of the component.
* @return the object name of the component
*/
public synchronized ObjectName getObjectName() {
return objectName;
}
/**
* Opens a connection to the component. This cretes the proxy used to communicate with the component
*/
public synchronized void open() {
if(open) {
throw new IllegalStateException("Connection is already open");
}
invocationHandler = new ConnectionInvocationHandler(new HashMap(methodMap), server, objectName);
proxy = Proxy.newProxyInstance(iface.getClassLoader(), new Class[]{iface}, invocationHandler);
open = true;
}
/**
* Closes the connection to the component.
*/
public synchronized void close() {
if(!open) {
throw new IllegalStateException("Connection is already closed");
}
invocationHandler.invalidate();
invocationHandler = null;
proxy = null;
open = false;
}
private static class ConnectionInvocationHandler implements InvocationHandler {
private boolean valid;
private Map methodMap;
private MBeanServer server;
private ObjectName objectName;
public ConnectionInvocationHandler(Map methodMap, MBeanServer server, ObjectName objectName) {
valid = true;
this.methodMap = methodMap;
this.server = server;
this.objectName = objectName;
}
public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
MBeanServer server;
ObjectName objectName;
MBeanInvoker mbeanInvoker;
// grab references to the variables in a static block
synchronized (this) {
if (!valid) {
throw new IllegalStateException("This proxy has been invalidated");
}
server = this.server;
objectName = this.objectName;
mbeanInvoker = (MBeanInvoker) methodMap.get(method);
}
return mbeanInvoker.invoke(server, objectName, arguments);
}
public synchronized void invalidate() {
valid = false;
server = null;
objectName = null;
methodMap.clear();
methodMap = null;
}
}
private static class MBeanInvoker {
private final String methodName;
private final String[] argumentTypes;
private final Class[] declaredExceptions;
public MBeanInvoker(Method method) {
methodName = method.getName();
// conver the parameters to a MBeanServer friendly string array
Class[] parameters = method.getParameterTypes();
argumentTypes = new String[parameters.length];
for (int i = 0; i < parameters.length; i++) {
argumentTypes[i] = parameters[i].getName();
}
declaredExceptions = method.getExceptionTypes();
}
public Object invoke(MBeanServer server, ObjectName objectName, Object[] arguments) throws Throwable {
try {
return server.invoke(objectName, methodName, arguments, argumentTypes);
} catch (Throwable t) {
Throwable throwable = t;
while (true) {
for (int i = 0; i < declaredExceptions.length; i++) {
Class declaredException = declaredExceptions[i];
if (declaredException.isInstance(throwable)) {
throw throwable;
}
}
// Unwrap the exceptions we understand
if (throwable instanceof MBeanException) {
throwable = (((MBeanException) throwable).getTargetException());
} else if (throwable instanceof ReflectionException) {
throwable = (((ReflectionException) throwable).getTargetException());
} else if (throwable instanceof RuntimeOperationsException) {
throwable = (((RuntimeOperationsException) throwable).getTargetException());
} else if (throwable instanceof RuntimeMBeanException) {
throwable = (((RuntimeMBeanException) throwable).getTargetException());
} else if (throwable instanceof RuntimeErrorException) {
throwable = (((RuntimeErrorException) throwable).getTargetError());
} else {
// don't know how to unwrap this, just throw it
throw throwable;
}
}
}
}
}
}