This commit is contained in:
Tim Bell 2008-09-04 14:55:12 -07:00
commit 710733eaa5
161 changed files with 30577 additions and 2881 deletions

View file

@ -158,6 +158,7 @@ CORE_PKGS = \
javax.management.event \
javax.management.loading \
javax.management.monitor \
javax.management.namespace \
javax.management.relation \
javax.management.openmbean \
javax.management.timer \

View file

@ -222,8 +222,6 @@ SUNWprivate_1.1 {
Java_java_lang_UNIXProcess_waitForProcessExit;
Java_java_lang_UNIXProcess_forkAndExec;
Java_java_lang_UNIXProcess_destroyProcess;
Java_java_nio_Bits_copyFromByteArray;
Java_java_nio_Bits_copyToByteArray;
Java_java_nio_Bits_copyFromShortArray;
Java_java_nio_Bits_copyToShortArray;
Java_java_nio_Bits_copyFromIntArray;

View file

@ -39,6 +39,9 @@ FILES_src = \
java/nio/channels/FileLock.java \
java/nio/channels/GatheringByteChannel.java \
java/nio/channels/InterruptibleChannel.java \
java/nio/channels/MembershipKey.java \
java/nio/channels/MulticastChannel.java \
java/nio/channels/NetworkChannel.java \
java/nio/channels/ReadableByteChannel.java \
java/nio/channels/ScatteringByteChannel.java \
java/nio/channels/SelectableChannel.java \
@ -73,6 +76,7 @@ FILES_src = \
sun/nio/ch/DatagramSocketAdaptor.java \
sun/nio/ch/DefaultSelectorProvider.java \
sun/nio/ch/DirectBuffer.java \
sun/nio/ch/ExtendedSocketOption.java \
sun/nio/ch/FileChannelImpl.java \
sun/nio/ch/FileDispatcher.java \
sun/nio/ch/FileKey.java \
@ -80,12 +84,14 @@ FILES_src = \
sun/nio/ch/IOUtil.java \
sun/nio/ch/IOStatus.java \
sun/nio/ch/IOVecWrapper.java \
sun/nio/ch/MembershipKeyImpl.java \
sun/nio/ch/MembershipRegistry.java \
sun/nio/ch/NativeDispatcher.java \
sun/nio/ch/NativeObject.java \
sun/nio/ch/NativeThread.java \
sun/nio/ch/NativeThreadSet.java \
sun/nio/ch/Net.java \
sun/nio/ch/OptionAdaptor.java \
sun/nio/ch/OptionKey.java \
sun/nio/ch/PipeImpl.java \
sun/nio/ch/PollArrayWrapper.java \
sun/nio/ch/Reflect.java \
@ -99,8 +105,7 @@ FILES_src = \
sun/nio/ch/SocketAdaptor.java \
sun/nio/ch/SocketChannelImpl.java \
sun/nio/ch/SocketDispatcher.java \
sun/nio/ch/SocketOpts.java \
sun/nio/ch/SocketOptsImpl.java \
sun/nio/ch/SocketOptionRegistry.java \
sun/nio/ch/SourceChannelImpl.java \
sun/nio/ch/Util.java \
\
@ -240,6 +245,7 @@ FILES_gen_ex = \
java/nio/InvalidMarkException.java \
java/nio/ReadOnlyBufferException.java \
\
java/nio/channels/AlreadyBoundException.java \
java/nio/channels/AlreadyConnectedException.java \
java/nio/channels/AsynchronousCloseException.java \
java/nio/channels/ClosedByInterruptException.java \
@ -258,14 +264,15 @@ FILES_gen_ex = \
java/nio/channels/UnresolvedAddressException.java \
java/nio/channels/UnsupportedAddressTypeException.java \
\
sun/nio/ch/AlreadyBoundException.java \
\
java/nio/charset/CharacterCodingException.java \
java/nio/charset/IllegalCharsetNameException.java \
java/nio/charset/UnsupportedCharsetException.java
FILES_gen_csp = sun/nio/cs/StandardCharsets.java
FILES_gen = $(FILES_gen_coder) $(FILES_gen_buffer) $(FILES_gen_ex) $(FILES_gen_csp)
FILES_gen_sor = sun/nio/ch/SocketOptionRegistry.java
FILES_gen = $(FILES_gen_coder) $(FILES_gen_buffer) $(FILES_gen_ex) \
$(FILES_gen_csp) $(FILES_gen_sor)
FILES_java = $(FILES_src) $(FILES_gen)

View file

@ -61,8 +61,8 @@ FILES_java += \
FILES_c += \
DevPollArrayWrapper.c \
InheritedChannel.c \
PollArrayWrapper.c \
NativeThread.c
NativeThread.c \
PollArrayWrapper.c
FILES_export += \
sun/nio/ch/DevPollArrayWrapper.java \
@ -94,9 +94,9 @@ FILES_java += \
FILES_c += \
EPollArrayWrapper.c \
PollArrayWrapper.c \
InheritedChannel.c \
NativeThread.c
NativeThread.c \
PollArrayWrapper.c
FILES_export += \
sun/nio/ch/EPollArrayWrapper.java \
@ -618,12 +618,6 @@ $(BUF_GEN)/%Exception.java: genExceptions.sh $(BUF_SRC)/exceptions
@$(RM) $@.temp
$(GEN_EX_CMD) $(BUF_SRC)/exceptions $(BUF_GEN)
$(SCH_GEN)/%Exception.java: genExceptions.sh $(SCH_SRC)/exceptions
$(prep-target)
@$(RM) $@.temp
$(GEN_EX_CMD) $(SCH_SRC)/exceptions $(SCH_GEN)
#
# Generated charset-provider classes
#
@ -638,4 +632,29 @@ $(SCS_GEN)/StandardCharsets.java: genCharsetProvider.sh \
HASHER="$(BOOT_JAVA_CMD) -jar $(HASHER_JARFILE)" \
$(SH) -e genCharsetProvider.sh $(SCS_SRC)/standard-charsets $(SCS_GEN)
#
# Generated channel implementation classes.
# C source is compiled in TEMPDIR to avoid turds left by Windows compilers.
#
GENSOR_SRC = $(SHARE_SRC)/native/sun/nio/ch/genSocketOptionRegistry.c
GENSOR_EXE = $(TEMPDIR)/genSocketOptionRegistry$(EXE_SUFFIX)
SOR_COPYRIGHT_YEARS = $(shell $(CAT) $(GENSOR_SRC) | \
$(NAWK) '/^.*Copyright.*Sun/ { print $$3 }')
$(TEMPDIR)/$(GENSOR_SRC) : $(GENSOR_SRC)
$(install-file)
$(GENSOR_EXE) : $(TEMPDIR)/$(GENSOR_SRC)
$(prep-target)
($(CD) $(TEMPDIR); $(CC) $(CPPFLAGS) $(LDDFLAGS) \
-o genSocketOptionRegistry$(EXE_SUFFIX) $(GENSOR_SRC))
$(SCH_GEN)/SocketOptionRegistry.java: $(GENSOR_EXE)
$(prep-target)
NAWK="$(NAWK)" SH="$(SH)" $(SH) -e addNotices.sh $(SOR_COPYRIGHT_YEARS) > $@
$(GENSOR_EXE) >> $@
.PHONY: sources

View file

@ -1,3 +1,27 @@
#
# Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Sun designates this
# particular file as subject to the "Classpath" exception as provided
# by Sun in the LICENSE file that accompanied this code.
#
# This code 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
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
# CA 95054 USA or visit www.sun.com if you need additional information or
# have any questions.
#
SUNWprivate_1.1 {
global:
@ -61,20 +85,29 @@ SUNWprivate_1.1 {
Java_sun_nio_ch_NativeThread_init;
Java_sun_nio_ch_NativeThread_signal;
Java_sun_nio_ch_Net_socket0;
Java_sun_nio_ch_Net_bind;
Java_sun_nio_ch_Net_connect;
Java_sun_nio_ch_Net_bind0;
Java_sun_nio_ch_Net_connect0;
Java_sun_nio_ch_Net_listen;
Java_sun_nio_ch_Net_localPort;
Java_sun_nio_ch_Net_localInetAddress;
Java_sun_nio_ch_Net_getIntOption0;
Java_sun_nio_ch_Net_setIntOption0;
Java_sun_nio_ch_Net_initIDs;
Java_sun_nio_ch_Net_isIPv6Available0;
Java_sun_nio_ch_Net_joinOrDrop4;
Java_sun_nio_ch_Net_blockOrUnblock4;
Java_sun_nio_ch_Net_joinOrDrop6;
Java_sun_nio_ch_Net_blockOrUnblock6;
Java_sun_nio_ch_Net_setInterface4;
Java_sun_nio_ch_Net_getInterface4;
Java_sun_nio_ch_Net_setInterface6;
Java_sun_nio_ch_Net_getInterface6;
Java_sun_nio_ch_Net_shutdown;
Java_sun_nio_ch_PollArrayWrapper_interrupt;
Java_sun_nio_ch_PollArrayWrapper_poll0;
Java_sun_nio_ch_ServerSocketChannelImpl_accept0;
Java_sun_nio_ch_ServerSocketChannelImpl_initIDs;
Java_sun_nio_ch_ServerSocketChannelImpl_listen;
Java_sun_nio_ch_SocketChannelImpl_checkConnect;
Java_sun_nio_ch_SocketChannelImpl_shutdown;
local:
*;

View file

@ -1,3 +1,27 @@
#
# Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Sun designates this
# particular file as subject to the "Classpath" exception as provided
# by Sun in the LICENSE file that accompanied this code.
#
# This code 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
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
# CA 95054 USA or visit www.sun.com if you need additional information or
# have any questions.
#
SUNWprivate_1.1 {
global:
@ -59,20 +83,29 @@ SUNWprivate_1.1 {
Java_sun_nio_ch_NativeThread_init;
Java_sun_nio_ch_NativeThread_signal;
Java_sun_nio_ch_Net_socket0;
Java_sun_nio_ch_Net_bind;
Java_sun_nio_ch_Net_connect;
Java_sun_nio_ch_Net_bind0;
Java_sun_nio_ch_Net_connect0;
Java_sun_nio_ch_Net_listen;
Java_sun_nio_ch_Net_localPort;
Java_sun_nio_ch_Net_localInetAddress;
Java_sun_nio_ch_Net_getIntOption0;
Java_sun_nio_ch_Net_setIntOption0;
Java_sun_nio_ch_Net_initIDs;
Java_sun_nio_ch_Net_isIPv6Available0;
Java_sun_nio_ch_Net_joinOrDrop4;
Java_sun_nio_ch_Net_blockOrUnblock4;
Java_sun_nio_ch_Net_joinOrDrop6;
Java_sun_nio_ch_Net_blockOrUnblock6;
Java_sun_nio_ch_Net_setInterface4;
Java_sun_nio_ch_Net_getInterface4;
Java_sun_nio_ch_Net_setInterface6;
Java_sun_nio_ch_Net_getInterface6;
Java_sun_nio_ch_Net_shutdown;
Java_sun_nio_ch_PollArrayWrapper_interrupt;
Java_sun_nio_ch_PollArrayWrapper_poll0;
Java_sun_nio_ch_ServerSocketChannelImpl_accept0;
Java_sun_nio_ch_ServerSocketChannelImpl_initIDs;
Java_sun_nio_ch_ServerSocketChannelImpl_listen;
Java_sun_nio_ch_SocketChannelImpl_checkConnect;
Java_sun_nio_ch_SocketChannelImpl_shutdown;
local:
*;

View file

@ -31,7 +31,7 @@ BUILDDIR = ../..
PRODUCT = java
include $(BUILDDIR)/common/Defs.gmk
SUBDIRS = server
SUBDIRS = multicast server
all build clean clobber::
$(SUBDIRS-loop)

View file

@ -1,5 +1,5 @@
#
# Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
# Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@ -23,17 +23,30 @@
# have any questions.
#
# Generated exception classes for sun.nio.ch
#
# Makefile for the nio/multicast sample code
#
SINCE=1.4
PACKAGE=sun.nio.ch
# This year should only change if the generated source is modified.
COPYRIGHT_YEARS=2000-2007
BUILDDIR = ../../..
PRODUCT = java
SUPER=IllegalStateException
include $(BUILDDIR)/common/Defs.gmk
gen AlreadyBoundException "
* Unchecked exception thrown when an attempt is made to bind a {@link
* SocketChannel} that is already bound." \
9002280723481772026L
SAMPLE_SRC_DIR = $(SHARE_SRC)/sample/nio/multicast
SAMPLE_DST_DIR = $(SAMPLEDIR)/nio/multicast
SAMPLE_FILES = \
$(SAMPLE_DST_DIR)/Reader.java \
$(SAMPLE_DST_DIR)/Sender.java \
$(SAMPLE_DST_DIR)/MulticastAddress.java
all build: $(SAMPLE_FILES)
$(SAMPLE_DST_DIR)/%: $(SAMPLE_SRC_DIR)/%
$(install-file)
clean clobber:
$(RM) -r $(SAMPLE_DST_DIR)
.PHONY: all build clean clobber

View file

@ -176,6 +176,18 @@ public class JmxProperties {
public static final String RELATION_LOGGER_NAME =
"javax.management.relation";
/**
* Logger name for Namespaces.
*/
public static final String NAMESPACE_LOGGER_NAME =
"javax.management.namespace";
/**
* Logger name for Namespaces.
*/
public static final Logger NAMESPACE_LOGGER =
Logger.getLogger(NAMESPACE_LOGGER_NAME);
/**
* Logger for Relation Service.
*/

View file

@ -25,33 +25,49 @@
package com.sun.jmx.interceptor;
// java import
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.Set;
import java.util.HashSet;
import java.util.WeakHashMap;
// JMX RI
import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
import com.sun.jmx.mbeanserver.DynamicMBean2;
import com.sun.jmx.mbeanserver.Introspector;
import com.sun.jmx.mbeanserver.MBeanInjector;
import com.sun.jmx.mbeanserver.MBeanInstantiator;
import com.sun.jmx.mbeanserver.ModifiableClassLoaderRepository;
import com.sun.jmx.mbeanserver.NamedObject;
import com.sun.jmx.mbeanserver.NotifySupport;
import com.sun.jmx.mbeanserver.Repository;
import com.sun.jmx.mbeanserver.Repository.RegistrationContext;
import com.sun.jmx.mbeanserver.Util;
import com.sun.jmx.remote.util.EnvHelp;
import java.lang.ref.WeakReference;
import java.security.AccessControlContext;
import java.security.Permission;
import java.security.ProtectionDomain;
import java.security.AccessController;
import java.security.Permission;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.logging.Level;
// JMX import
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.DynamicWrapperMBean;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.JMRuntimeException;
import javax.management.ListenerNotFoundException;
import javax.management.MalformedObjectNameException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanPermission;
@ -64,6 +80,7 @@ import javax.management.MBeanTrustPermission;
import javax.management.NotCompliantMBeanException;
import javax.management.Notification;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
@ -75,22 +92,7 @@ import javax.management.ReflectionException;
import javax.management.RuntimeErrorException;
import javax.management.RuntimeMBeanException;
import javax.management.RuntimeOperationsException;
// JMX RI
import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
import com.sun.jmx.mbeanserver.DynamicMBean2;
import com.sun.jmx.mbeanserver.ModifiableClassLoaderRepository;
import com.sun.jmx.mbeanserver.MBeanInstantiator;
import com.sun.jmx.mbeanserver.Repository;
import com.sun.jmx.mbeanserver.NamedObject;
import com.sun.jmx.mbeanserver.Introspector;
import com.sun.jmx.mbeanserver.MBeanInjector;
import com.sun.jmx.mbeanserver.NotifySupport;
import com.sun.jmx.mbeanserver.Repository.RegistrationContext;
import com.sun.jmx.mbeanserver.Util;
import com.sun.jmx.remote.util.EnvHelp;
import javax.management.DynamicWrapperMBean;
import javax.management.NotificationBroadcasterSupport;
import javax.management.namespace.JMXNamespace;
/**
* This is the default class for MBean manipulation on the agent side. It
@ -113,7 +115,8 @@ import javax.management.NotificationBroadcasterSupport;
*
* @since 1.5
*/
public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
public class DefaultMBeanServerInterceptor
extends MBeanServerInterceptorSupport {
/** The MBeanInstantiator object used by the
* DefaultMBeanServerInterceptor */
@ -123,7 +126,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
* DefaultMBeanServerInterceptor */
private transient MBeanServer server = null;
/** The MBean server object taht associated to the
/** The MBean server delegate object that is associated to the
* DefaultMBeanServerInterceptor */
private final transient MBeanServerDelegate delegate;
@ -138,13 +141,15 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
new WeakHashMap<ListenerWrapper,
WeakReference<ListenerWrapper>>();
private final NamespaceDispatchInterceptor dispatcher;
/** The default domain of the object names */
private final String domain;
/** True if the repository perform queries, false otherwise */
private boolean queryByRepo;
/** The mbeanServerName */
private final String mbeanServerName;
/** The sequence number identifyng the notifications sent */
/** The sequence number identifying the notifications sent */
// Now sequence number is handled by MBeanServerDelegate.
// private int sequenceNumber=0;
@ -162,11 +167,13 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
* @param instantiator The MBeanInstantiator that will be used to
* instantiate MBeans and take care of class loading issues.
* @param repository The repository to use for this MBeanServer.
* @param dispatcher The dispatcher used by this MBeanServer
*/
public DefaultMBeanServerInterceptor(MBeanServer outer,
MBeanServerDelegate delegate,
MBeanInstantiator instantiator,
Repository repository) {
Repository repository,
NamespaceDispatchInterceptor dispatcher) {
if (outer == null) throw new
IllegalArgumentException("outer MBeanServer cannot be null");
if (delegate == null) throw new
@ -181,6 +188,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
this.instantiator = instantiator;
this.repository = repository;
this.domain = repository.getDefaultDomain();
this.dispatcher = dispatcher;
this.mbeanServerName = Util.getMBeanServerSecurityName(delegate);
}
public ObjectInstance createMBean(String className, ObjectName name)
@ -259,8 +268,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
name = nonDefaultDomain(name);
}
checkMBeanPermission(className, null, null, "instantiate");
checkMBeanPermission(className, null, name, "registerMBean");
checkMBeanPermission(mbeanServerName,className, null, null, "instantiate");
checkMBeanPermission(mbeanServerName,className, null, name, "registerMBean");
/* Load the appropriate class. */
if (withDefaultLoaderRepository) {
@ -324,7 +333,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
final String infoClassName = getNewMBeanClassName(object);
checkMBeanPermission(infoClassName, null, name, "registerMBean");
checkMBeanPermission(mbeanServerName,infoClassName, null, name, "registerMBean");
checkMBeanTrustPermission(theClass);
return registerObject(infoClassName, object, name);
@ -433,7 +442,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
DynamicMBean instance = getMBean(name);
// may throw InstanceNotFoundException
checkMBeanPermission(instance, null, name, "unregisterMBean");
checkMBeanPermission(mbeanServerName, instance, null, name,
"unregisterMBean");
if (instance instanceof MBeanRegistration)
preDeregisterInvoke((MBeanRegistration) instance);
@ -467,7 +477,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
name = nonDefaultDomain(name);
DynamicMBean instance = getMBean(name);
checkMBeanPermission(instance, null, name, "getObjectInstance");
checkMBeanPermission(mbeanServerName,
instance, null, name, "getObjectInstance");
final String className = getClassName(instance);
@ -479,7 +490,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
if (sm != null) {
// Check if the caller has the right to invoke 'queryMBeans'
//
checkMBeanPermission((String) null, null, null, "queryMBeans");
checkMBeanPermission(mbeanServerName,(String) null, null, null, "queryMBeans");
// Perform query without "query".
//
@ -492,7 +503,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
new HashSet<ObjectInstance>(list.size());
for (ObjectInstance oi : list) {
try {
checkMBeanPermission(oi.getClassName(), null,
checkMBeanPermission(mbeanServerName,oi.getClassName(), null,
oi.getObjectName(), "queryMBeans");
allowedList.add(oi);
} catch (SecurityException e) {
@ -516,11 +527,6 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
//
Set<NamedObject> list = repository.query(name, query);
if (queryByRepo) {
// The repository performs the filtering
query = null;
}
return (objectInstancesFromFilteredNamedObjects(list, query));
}
@ -530,7 +536,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
if (sm != null) {
// Check if the caller has the right to invoke 'queryNames'
//
checkMBeanPermission((String) null, null, null, "queryNames");
checkMBeanPermission(mbeanServerName,(String) null, null, null, "queryNames");
// Perform query without "query".
//
@ -543,7 +549,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
new HashSet<ObjectInstance>(list.size());
for (ObjectInstance oi : list) {
try {
checkMBeanPermission(oi.getClassName(), null,
checkMBeanPermission(mbeanServerName, oi.getClassName(), null,
oi.getObjectName(), "queryNames");
allowedList.add(oi);
} catch (SecurityException e) {
@ -572,11 +578,6 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
//
Set<NamedObject> list = repository.query(name, query);
if (queryByRepo) {
// The repository performs the filtering
query = null;
}
return (objectNamesFromFilteredNamedObjects(list, query));
}
@ -589,8 +590,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
name = nonDefaultDomain(name);
// /* Permission check */
// checkMBeanPermission(null, null, name, "isRegistered");
/* No Permission check */
// isRegistered is always unchecked as per JMX spec.
return (repository.contains(name));
}
@ -600,7 +601,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
if (sm != null) {
// Check if the caller has the right to invoke 'getDomains'
//
checkMBeanPermission((String) null, null, null, "getDomains");
checkMBeanPermission(mbeanServerName, (String) null, null, null, "getDomains");
// Return domains
//
@ -612,8 +613,9 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
List<String> result = new ArrayList<String>(domains.length);
for (int i = 0; i < domains.length; i++) {
try {
ObjectName domain = Util.newObjectName(domains[i] + ":x=x");
checkMBeanPermission((String) null, null, domain, "getDomains");
ObjectName dom =
Util.newObjectName(domains[i] + ":x=x");
checkMBeanPermission(mbeanServerName, (String) null, null, dom, "getDomains");
result.add(domains[i]);
} catch (SecurityException e) {
// OK: Do not add this domain to the list
@ -657,7 +659,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
}
final DynamicMBean instance = getMBean(name);
checkMBeanPermission(instance, attribute, name, "getAttribute");
checkMBeanPermission(mbeanServerName, instance, attribute,
name, "getAttribute");
try {
return instance.getAttribute(attribute);
@ -702,7 +705,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
// Check if the caller has the right to invoke 'getAttribute'
//
checkMBeanPermission(classname, null, name, "getAttribute");
checkMBeanPermission(mbeanServerName, classname, null, name, "getAttribute");
// Check if the caller has the right to invoke 'getAttribute'
// on each specific attribute
@ -711,14 +714,15 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
new ArrayList<String>(attributes.length);
for (String attr : attributes) {
try {
checkMBeanPermission(classname, attr,
checkMBeanPermission(mbeanServerName, classname, attr,
name, "getAttribute");
allowedList.add(attr);
} catch (SecurityException e) {
// OK: Do not add this attribute to the list
}
}
allowedAttributes = allowedList.toArray(new String[0]);
allowedAttributes =
allowedList.toArray(new String[allowedList.size()]);
}
try {
@ -756,7 +760,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
}
DynamicMBean instance = getMBean(name);
checkMBeanPermission(instance, attribute.getName(),
checkMBeanPermission(mbeanServerName, instance, attribute.getName(),
name, "setAttribute");
try {
@ -799,7 +803,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
// Check if the caller has the right to invoke 'setAttribute'
//
checkMBeanPermission(classname, null, name, "setAttribute");
checkMBeanPermission(mbeanServerName, classname, null, name, "setAttribute");
// Check if the caller has the right to invoke 'setAttribute'
// on each specific attribute
@ -808,7 +812,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
for (Iterator i = attributes.iterator(); i.hasNext();) {
try {
Attribute attribute = (Attribute) i.next();
checkMBeanPermission(classname, attribute.getName(),
checkMBeanPermission(mbeanServerName, classname, attribute.getName(),
name, "setAttribute");
allowedAttributes.add(attribute);
} catch (SecurityException e) {
@ -832,7 +836,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
name = nonDefaultDomain(name);
DynamicMBean instance = getMBean(name);
checkMBeanPermission(instance, operationName, name, "invoke");
checkMBeanPermission(mbeanServerName, instance, operationName,
name, "invoke");
try {
return instance.invoke(operationName, params, signature);
} catch (Throwable t) {
@ -934,8 +939,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
"registerMBean", "ObjectName = " + name);
}
ObjectName logicalName = name;
logicalName = preRegister(mbean, server, name);
ObjectName logicalName = preRegister(mbean, server, name);
// preRegister returned successfully, so from this point on we
// must call postRegister(false) if there is any problem.
@ -964,7 +968,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
ObjectName.getInstance(nonDefaultDomain(logicalName));
}
checkMBeanPermission(classname, null, logicalName, "registerMBean");
checkMBeanPermission(mbeanServerName, classname, null, logicalName,
"registerMBean");
if (logicalName == null) {
final RuntimeException wrapped =
@ -987,13 +992,15 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
//
context = registerWithRepository(resource, mbean, logicalName);
registerFailed = false;
registered = true;
} finally {
try {
postRegister(logicalName, mbean, registered, registerFailed);
} finally {
if (registered) context.done();
if (registered && context!=null) context.done();
}
}
return new ObjectInstance(logicalName, classname);
@ -1001,20 +1008,19 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
private static void throwMBeanRegistrationException(Throwable t, String where)
throws MBeanRegistrationException {
try {
throw t;
} catch (RuntimeException e) {
throw new RuntimeMBeanException(
e, "RuntimeException thrown " + where);
} catch (Error er) {
throw new RuntimeErrorException(er, "Error thrown " + where);
} catch (MBeanRegistrationException r) {
throw r;
} catch (Exception ex) {
throw new MBeanRegistrationException(ex, "Exception thrown " + where);
} catch (Throwable t1) {
throw new RuntimeException(t); // neither Error nor Exception??
}
if (t instanceof RuntimeException) {
throw new RuntimeMBeanException((RuntimeException)t,
"RuntimeException thrown " + where);
} else if (t instanceof Error) {
throw new RuntimeErrorException((Error)t,
"Error thrown " + where);
} else if (t instanceof MBeanRegistrationException) {
throw (MBeanRegistrationException)t;
} else if (t instanceof Exception) {
throw new MBeanRegistrationException((Exception)t,
"Exception thrown " + where);
} else // neither Error nor Exception??
throw new RuntimeException(t);
}
private static ObjectName preRegister(
@ -1230,7 +1236,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
}
DynamicMBean instance = getMBean(name);
checkMBeanPermission(instance, null, name, "addNotificationListener");
checkMBeanPermission(mbeanServerName, instance, null,
name, "addNotificationListener");
NotificationBroadcaster broadcaster =
getNotificationBroadcaster(name, instance,
@ -1367,7 +1374,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
}
DynamicMBean instance = getMBean(name);
checkMBeanPermission(instance, null, name,
checkMBeanPermission(mbeanServerName, instance, null, name,
"removeNotificationListener");
/* We could simplify the code by assigning broadcaster after
@ -1438,7 +1445,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
throw new JMRuntimeException("MBean " + name +
"has no MBeanInfo");
checkMBeanPermission(mbi.getClassName(), null, name, "getMBeanInfo");
checkMBeanPermission(mbeanServerName, mbi.getClassName(), null, name, "getMBeanInfo");
return mbi;
}
@ -1446,8 +1453,9 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
public boolean isInstanceOf(ObjectName name, String className)
throws InstanceNotFoundException {
DynamicMBean instance = getMBean(name);
checkMBeanPermission(instance, null, name, "isInstanceOf");
final DynamicMBean instance = getMBean(name);
checkMBeanPermission(mbeanServerName,
instance, null, name, "isInstanceOf");
try {
Object resource = getResource(instance);
@ -1498,7 +1506,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
throws InstanceNotFoundException {
DynamicMBean instance = getMBean(mbeanName);
checkMBeanPermission(instance, null, mbeanName, "getClassLoaderFor");
checkMBeanPermission(mbeanServerName, instance, null, mbeanName,
"getClassLoaderFor");
return getResourceLoader(instance);
}
@ -1513,12 +1522,13 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
throws InstanceNotFoundException {
if (loaderName == null) {
checkMBeanPermission((String) null, null, null, "getClassLoader");
checkMBeanPermission(mbeanServerName, (String) null, null, null, "getClassLoader");
return server.getClass().getClassLoader();
}
DynamicMBean instance = getMBean(loaderName);
checkMBeanPermission(instance, null, loaderName, "getClassLoader");
checkMBeanPermission(mbeanServerName, instance, null, loaderName,
"getClassLoader");
Object resource = getResource(instance);
@ -1568,7 +1578,7 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
}
} else {
// Access the filter
MBeanServer oldServer = QueryEval.getMBeanServer();
final MBeanServer oldServer = QueryEval.getMBeanServer();
query.setMBeanServer(server);
try {
for (NamedObject no : list) {
@ -1817,26 +1827,30 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
return mbean.getMBeanInfo().getClassName();
}
private static void checkMBeanPermission(DynamicMBean mbean,
private static void checkMBeanPermission(String mbeanServerName,
DynamicMBean mbean,
String member,
ObjectName objectName,
String actions) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMBeanPermission(safeGetClassName(mbean),
checkMBeanPermission(mbeanServerName,
safeGetClassName(mbean),
member,
objectName,
actions);
}
}
private static void checkMBeanPermission(String classname,
private static void checkMBeanPermission(String mbeanServerName,
String classname,
String member,
ObjectName objectName,
String actions) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
Permission perm = new MBeanPermission(classname,
Permission perm = new MBeanPermission(mbeanServerName,
classname,
member,
objectName,
actions);
@ -1902,6 +1916,12 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
throws InstanceAlreadyExistsException,
MBeanRegistrationException {
// this will throw an exception if the pair (resource, logicalName)
// violates namespace conventions - for instance, if logicalName
// ends with // but resource is not a JMXNamespace.
//
checkResourceObjectNameConstraints(resource, logicalName);
// Creates a registration context, if needed.
//
final ResourceContext context =
@ -1967,6 +1987,57 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
return context;
}
/**
* Checks that the ObjectName is legal with regards to the
* type of the MBean resource.
* If the MBean name is domain:type=JMXDomain, the
* MBean must be a JMXDomain.
* If the MBean name is namespace//:type=JMXNamespace, the
* MBean must be a JMXNamespace.
* If the MBean is a JMXDomain, its name
* must be domain:type=JMXDomain.
* If the MBean is a JMXNamespace, its name
* must be namespace//:type=JMXNamespace.
*/
private void checkResourceObjectNameConstraints(Object resource,
ObjectName logicalName)
throws MBeanRegistrationException {
try {
dispatcher.checkLocallyRegistrable(resource, logicalName);
} catch (Throwable x) {
DefaultMBeanServerInterceptor.throwMBeanRegistrationException(x, "validating ObjectName");
}
}
/**
* Registers a JMXNamespace with the dispatcher.
* This method is called by the ResourceContext from within the
* repository lock.
* @param namespace The JMXNamespace
* @param logicalName The JMXNamespaceMBean ObjectName
* @param postQueue A queue that will be processed after postRegister.
*/
private void addJMXNamespace(JMXNamespace namespace,
final ObjectName logicalName,
final Queue<Runnable> postQueue) {
dispatcher.addNamespace(logicalName, namespace, postQueue);
}
/**
* Unregisters a JMXNamespace from the dispatcher.
* This method is called by the ResourceContext from within the
* repository lock.
* @param namespace The JMXNamespace
* @param logicalName The JMXNamespaceMBean ObjectName
* @param postQueue A queue that will be processed after postDeregister.
*/
private void removeJMXNamespace(JMXNamespace namespace,
final ObjectName logicalName,
final Queue<Runnable> postQueue) {
dispatcher.removeNamespace(logicalName, namespace, postQueue);
}
/**
* Registers a ClassLoader with the CLR.
* This method is called by the ResourceContext from within the
@ -2020,6 +2091,52 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
}
}
/**
* Creates a ResourceContext for a JMXNamespace MBean.
* The resource context makes it possible to add the JMXNamespace to
* (ResourceContext.registering) or resp. remove the JMXNamespace from
* (ResourceContext.unregistered) the NamespaceDispatchInterceptor
* when the associated MBean is added to or resp. removed from the
* repository.
* Note: JMXDomains are special sub classes of JMXNamespaces and
* are also handled by this object.
*
* @param namespace The JMXNamespace MBean being registered or
* unregistered.
* @param logicalName The name of the JMXNamespace MBean.
* @return a ResourceContext that takes in charge the addition or removal
* of the namespace to or from the NamespaceDispatchInterceptor.
*/
private ResourceContext createJMXNamespaceContext(
final JMXNamespace namespace,
final ObjectName logicalName) {
final Queue<Runnable> doneTaskQueue = new LinkedList<Runnable>();
return new ResourceContext() {
public void registering() {
addJMXNamespace(namespace, logicalName, doneTaskQueue);
}
public void unregistered() {
removeJMXNamespace(namespace, logicalName,
doneTaskQueue);
}
public void done() {
for (Runnable r : doneTaskQueue) {
try {
r.run();
} catch (RuntimeException x) {
MBEANSERVER_LOGGER.log(Level.FINE,
"Failed to process post queue for "+
logicalName, x);
}
}
}
};
}
/**
* Creates a ResourceContext for a ClassLoader MBean.
* The resource context makes it possible to add the ClassLoader to
@ -2065,10 +2182,16 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
*/
private ResourceContext makeResourceContextFor(Object resource,
ObjectName logicalName) {
if (resource instanceof JMXNamespace) {
return createJMXNamespaceContext((JMXNamespace) resource,
logicalName);
}
if (resource instanceof ClassLoader) {
return createClassLoaderContext((ClassLoader) resource,
logicalName);
}
return ResourceContext.NONE;
}
}

View file

@ -0,0 +1,547 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.interceptor;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.management.namespace.JMXNamespace;
/**
* A dispatcher that dispatches to MBeanServers.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
//
// This is the base class for implementing dispatchers. We have two concrete
// dispatcher implementations:
//
// * A NamespaceDispatchInterceptor, which dispatch calls to existing
// namespace interceptors
// * A DomainDispatchInterceptor, which dispatch calls to existing domain
// interceptors.
//
// With the JMX Namespaces feature, the JMX MBeanServer is now structured
// as follows:
//
// The JMX MBeanServer delegates to a NamespaceDispatchInterceptor,
// which either dispatches to a namespace, or delegates to the
// DomainDispatchInterceptor (if the object name contained no namespace).
// The DomainDispatchInterceptor in turn either dispatches to a domain (if
// there is a JMXDomain for that domain) or delegates to the
// DefaultMBeanServerInterceptor (if there is no JMXDomain for that
// domain). This makes the following picture:
//
// JMX MBeanServer (outer shell)
// |
// |
// NamespaceDispatchInterceptor
// / \
// no namespace in object name? \
// / \
// / dispatch to namespace
// DomainDispatchInterceptor
// / \
// no JMXDomain for domain? \
// / \
// / dispatch to domain
// DefaultMBeanServerInterceptor
// /
// invoke locally registered MBean
//
// The logic for maintaining a map of interceptors
// and dispatching to impacted interceptor, is implemented in this
// base class, which both NamespaceDispatchInterceptor and
// DomainDispatchInterceptor extend.
//
public abstract class DispatchInterceptor
<T extends MBeanServer, N extends JMXNamespace>
extends MBeanServerInterceptorSupport {
/**
* This is an abstraction which allows us to handle queryNames
* and queryMBeans with the same algorithm. There are some subclasses
* where we need to override both queryNames & queryMBeans to apply
* the same transformation (usually aggregation of results when
* several namespaces/domains are impacted) to both algorithms.
* Usually the only thing that varies between the algorithm of
* queryNames & the algorithm of queryMBean is the type of objects
* in the returned Set. By using a QueryInvoker we can implement the
* transformation only once and apply it to both queryNames &
* queryMBeans.
* @see QueryInterceptor below, and its subclass in
* {@link DomainDispatcher}.
**/
static abstract class QueryInvoker<T> {
abstract Set<T> query(MBeanServer mbs,
ObjectName pattern, QueryExp query);
}
/**
* Used to perform queryNames. A QueryInvoker that invokes
* queryNames on an MBeanServer.
**/
final static QueryInvoker<ObjectName> queryNamesInvoker =
new QueryInvoker<ObjectName>() {
Set<ObjectName> query(MBeanServer mbs,
ObjectName pattern, QueryExp query) {
return mbs.queryNames(pattern,query);
}
};
/**
* Used to perform queryMBeans. A QueryInvoker that invokes
* queryMBeans on an MBeanServer.
**/
final static QueryInvoker<ObjectInstance> queryMBeansInvoker =
new QueryInvoker<ObjectInstance>() {
Set<ObjectInstance> query(MBeanServer mbs,
ObjectName pattern, QueryExp query) {
return mbs.queryMBeans(pattern,query);
}
};
/**
* We use this class to intercept queries.
* There's a special case for JMXNamespace MBeans, because
* "namespace//*:*" matches both "namespace//domain:k=v" and
* "namespace//:type=JMXNamespace".
* Therefore, queries may need to be forwarded to more than
* on interceptor and the results aggregated...
*/
static class QueryInterceptor {
final MBeanServer wrapped;
QueryInterceptor(MBeanServer mbs) {
wrapped = mbs;
}
<X> Set<X> query(ObjectName pattern, QueryExp query,
QueryInvoker<X> invoker, MBeanServer server) {
return invoker.query(server, pattern, query);
}
public Set<ObjectName> queryNames(ObjectName pattern, QueryExp query) {
return query(pattern,query,queryNamesInvoker,wrapped);
}
public Set<ObjectInstance> queryMBeans(ObjectName pattern,
QueryExp query) {
return query(pattern,query,queryMBeansInvoker,wrapped);
}
}
// We don't need a ConcurrentHashMap here because getkeys() returns
// an array of keys. Therefore there's no risk to have a
// ConcurrentModificationException. We must however take into
// account the fact that there can be no interceptor for
// some of the returned keys if the map is being modified by
// another thread, or by a callback within the same thread...
// See getKeys() in this class and query() in DomainDispatcher.
//
private final Map<String,T> handlerMap =
Collections.synchronizedMap(
new HashMap<String,T>());
// The key at which an interceptor for accessing the named MBean can be
// found in the handlerMap. Note: there doesn't need to be an interceptor
// for that key in the Map.
//
public abstract String getHandlerKey(ObjectName name);
// Returns an interceptor for that name, or null if there's no interceptor
// for that name.
abstract MBeanServer getInterceptorOrNullFor(ObjectName name);
// Returns a QueryInterceptor for that pattern.
abstract QueryInterceptor getInterceptorForQuery(ObjectName pattern);
// Returns the ObjectName of the JMXNamespace (or JMXDomain) for that
// key (a namespace or a domain name).
abstract ObjectName getHandlerNameFor(String key)
throws MalformedObjectNameException;
// Creates an interceptor for the given key, name, JMXNamespace (or
// JMXDomain). Note: this will be either a NamespaceInterceptor
// wrapping a JMXNamespace, if this object is an instance of
// NamespaceDispatchInterceptor, or a DomainInterceptor wrapping a
// JMXDomain, if this object is an instance of DomainDispatchInterceptor.
abstract T createInterceptorFor(String key, ObjectName name,
N jmxNamespace, Queue<Runnable> postRegisterQueue);
//
// The next interceptor in the chain.
//
// For the NamespaceDispatchInterceptor, this the DomainDispatchInterceptor.
// For the DomainDispatchInterceptor, this is the
// DefaultMBeanServerInterceptor.
//
// The logic of when to invoke the next interceptor in the chain depends
// on the logic of the concrete dispatcher class.
//
// For instance, the NamespaceDispatchInterceptor invokes the next
// interceptor when the object name doesn't contain any namespace.
//
// On the other hand, the DomainDispatchInterceptor invokes the
// next interceptor when there's no interceptor for the accessed domain.
//
abstract MBeanServer getNextInterceptor();
// hook for cleanup in subclasses.
void interceptorReleased(T interceptor,
Queue<Runnable> postDeregisterQueue) {
// hook
}
// Hook for subclasses.
MBeanServer getInterceptorForCreate(ObjectName name)
throws MBeanRegistrationException {
final MBeanServer ns = getInterceptorOrNullFor(name);
if (ns == null) // name cannot be null here.
throw new MBeanRegistrationException(
new IllegalArgumentException("No such MBean handler: " +
getHandlerKey(name) + " for " +name));
return ns;
}
// Hook for subclasses.
MBeanServer getInterceptorForInstance(ObjectName name)
throws InstanceNotFoundException {
final MBeanServer ns = getInterceptorOrNullFor(name);
if (ns == null) // name cannot be null here.
throw new InstanceNotFoundException(String.valueOf(name));
return ns;
}
// sanity checks
void validateHandlerNameFor(String key, ObjectName name) {
if (key == null || key.equals(""))
throw new IllegalArgumentException("invalid key for "+name+": "+key);
try {
final ObjectName handlerName = getHandlerNameFor(key);
if (!name.equals(handlerName))
throw new IllegalArgumentException("bad handler name: "+name+
". Should be: "+handlerName);
} catch (MalformedObjectNameException x) {
throw new IllegalArgumentException(name.toString(),x);
}
}
// Called by the DefaultMBeanServerInterceptor when an instance
// of JMXNamespace (or a subclass of it) is registered as an MBean.
// This method is usually invoked from within the repository lock,
// hence the necessity of the postRegisterQueue.
public void addNamespace(ObjectName name, N jmxNamespace,
Queue<Runnable> postRegisterQueue) {
final String key = getHandlerKey(name);
validateHandlerNameFor(key,name);
synchronized (handlerMap) {
final T exists =
handlerMap.get(key);
if (exists != null)
throw new IllegalArgumentException(key+
": handler already exists");
final T ns = createInterceptorFor(key,name,jmxNamespace,
postRegisterQueue);
handlerMap.put(key,ns);
}
}
// Called by the DefaultMBeanServerInterceptor when an instance
// of JMXNamespace (or a subclass of it) is deregistered.
// This method is usually invoked from within the repository lock,
// hence the necessity of the postDeregisterQueue.
public void removeNamespace(ObjectName name, N jmxNamespace,
Queue<Runnable> postDeregisterQueue) {
final String key = getHandlerKey(name);
final T ns;
synchronized(handlerMap) {
ns = handlerMap.remove(key);
}
interceptorReleased(ns,postDeregisterQueue);
}
// Get the interceptor for that key.
T getInterceptor(String key) {
synchronized (handlerMap) {
return handlerMap.get(key);
}
}
// We return an array of keys, which makes it possible to make
// concurrent modifications of the handlerMap, provided that
// the code which loops over the keys is prepared to handle null
// interceptors.
// See declaration of handlerMap above, and see also query() in
// DomainDispatcher
//
public String[] getKeys() {
synchronized (handlerMap) {
final int size = handlerMap.size();
return handlerMap.keySet().toArray(new String[size]);
}
}
// From MBeanServer
public ObjectInstance createMBean(String className, ObjectName name)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException {
return getInterceptorForCreate(name).createMBean(className,name);
}
// From MBeanServer
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException, InstanceNotFoundException{
return getInterceptorForCreate(name).createMBean(className,name,loaderName);
}
// From MBeanServer
public ObjectInstance createMBean(String className, ObjectName name,
Object params[], String signature[])
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException{
return getInterceptorForCreate(name).
createMBean(className,name,params,signature);
}
// From MBeanServer
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName, Object params[],
String signature[])
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException, InstanceNotFoundException{
return getInterceptorForCreate(name).createMBean(className,name,loaderName,
params,signature);
}
// From MBeanServer
public ObjectInstance registerMBean(Object object, ObjectName name)
throws InstanceAlreadyExistsException, MBeanRegistrationException,
NotCompliantMBeanException {
return getInterceptorForCreate(name).registerMBean(object,name);
}
// From MBeanServer
public void unregisterMBean(ObjectName name)
throws InstanceNotFoundException, MBeanRegistrationException {
getInterceptorForInstance(name).unregisterMBean(name);
}
// From MBeanServer
public ObjectInstance getObjectInstance(ObjectName name)
throws InstanceNotFoundException {
return getInterceptorForInstance(name).getObjectInstance(name);
}
// From MBeanServer
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
final QueryInterceptor mbs =
getInterceptorForQuery(name);
if (mbs == null) return Collections.emptySet();
else return mbs.queryMBeans(name,query);
}
// From MBeanServer
public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
final QueryInterceptor mbs =
getInterceptorForQuery(name);
if (mbs == null) return Collections.emptySet();
else return mbs.queryNames(name,query);
}
// From MBeanServer
public boolean isRegistered(ObjectName name) {
final MBeanServer mbs = getInterceptorOrNullFor(name);
if (mbs == null) return false;
else return mbs.isRegistered(name);
}
// From MBeanServer
public Integer getMBeanCount() {
return getNextInterceptor().getMBeanCount();
}
// From MBeanServer
public Object getAttribute(ObjectName name, String attribute)
throws MBeanException, AttributeNotFoundException,
InstanceNotFoundException, ReflectionException {
return getInterceptorForInstance(name).getAttribute(name,attribute);
}
// From MBeanServer
public AttributeList getAttributes(ObjectName name, String[] attributes)
throws InstanceNotFoundException, ReflectionException {
return getInterceptorForInstance(name).getAttributes(name,attributes);
}
// From MBeanServer
public void setAttribute(ObjectName name, Attribute attribute)
throws InstanceNotFoundException, AttributeNotFoundException,
InvalidAttributeValueException, MBeanException,
ReflectionException {
getInterceptorForInstance(name).setAttribute(name,attribute);
}
// From MBeanServer
public AttributeList setAttributes(ObjectName name,
AttributeList attributes)
throws InstanceNotFoundException, ReflectionException {
return getInterceptorForInstance(name).setAttributes(name,attributes);
}
// From MBeanServer
public Object invoke(ObjectName name, String operationName,
Object params[], String signature[])
throws InstanceNotFoundException, MBeanException,
ReflectionException {
return getInterceptorForInstance(name).invoke(name,operationName,params,
signature);
}
// From MBeanServer
public String getDefaultDomain() {
return getNextInterceptor().getDefaultDomain();
}
/**
* Returns the list of domains in which any MBean is currently
* registered.
*/
public abstract String[] getDomains();
// From MBeanServer
public void addNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException {
getInterceptorForInstance(name).addNotificationListener(name,listener,filter,
handback);
}
// From MBeanServer
public void addNotificationListener(ObjectName name,
ObjectName listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException {
getInterceptorForInstance(name).addNotificationListener(name,listener,filter,
handback);
}
// From MBeanServer
public void removeNotificationListener(ObjectName name,
ObjectName listener)
throws InstanceNotFoundException, ListenerNotFoundException {
getInterceptorForInstance(name).removeNotificationListener(name,listener);
}
// From MBeanServer
public void removeNotificationListener(ObjectName name,
ObjectName listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException, ListenerNotFoundException {
getInterceptorForInstance(name).removeNotificationListener(name,listener,filter,
handback);
}
// From MBeanServer
public void removeNotificationListener(ObjectName name,
NotificationListener listener)
throws InstanceNotFoundException, ListenerNotFoundException {
getInterceptorForInstance(name).removeNotificationListener(name,listener);
}
// From MBeanServer
public void removeNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException, ListenerNotFoundException {
getInterceptorForInstance(name).removeNotificationListener(name,listener,filter,
handback);
}
// From MBeanServer
public MBeanInfo getMBeanInfo(ObjectName name)
throws InstanceNotFoundException, IntrospectionException,
ReflectionException {
return getInterceptorForInstance(name).getMBeanInfo(name);
}
// From MBeanServer
public boolean isInstanceOf(ObjectName name, String className)
throws InstanceNotFoundException {
return getInterceptorForInstance(name).isInstanceOf(name,className);
}
// From MBeanServer
public ClassLoader getClassLoaderFor(ObjectName mbeanName)
throws InstanceNotFoundException {
return getInterceptorForInstance(mbeanName).getClassLoaderFor(mbeanName);
}
// From MBeanServer
public ClassLoader getClassLoader(ObjectName loaderName)
throws InstanceNotFoundException {
return getInterceptorForInstance(loaderName).getClassLoader(loaderName);
}
}

View file

@ -0,0 +1,322 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.interceptor;
import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.mbeanserver.MBeanInstantiator;
import com.sun.jmx.mbeanserver.Repository;
import com.sun.jmx.mbeanserver.Util;
import com.sun.jmx.namespace.DomainInterceptor;
import java.util.Queue;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.MBeanServer;
import javax.management.MBeanServerDelegate;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.namespace.JMXDomain;
import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
/**
* A dispatcher that dispatch incoming MBeanServer requests to
* DomainInterceptors.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
//
// See comments in DispatchInterceptor.
//
class DomainDispatchInterceptor
extends DispatchInterceptor<DomainInterceptor, JMXDomain> {
/**
* A logger for this class.
**/
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
private static final ObjectName ALL_DOMAINS =
JMXDomain.getDomainObjectName("*");
/**
* A QueryInterceptor that perform & aggregates queries spanning several
* domains.
*/
final static class AggregatingQueryInterceptor extends QueryInterceptor {
private final DomainDispatchInterceptor parent;
AggregatingQueryInterceptor(DomainDispatchInterceptor dispatcher) {
super(dispatcher.localNamespace);
parent = dispatcher;
}
/**
* Perform queryNames or queryMBeans, depending on which QueryInvoker
* is passed as argument. This is closures without closures.
**/
@Override
<T> Set<T> query(ObjectName pattern, QueryExp query,
QueryInvoker<T> invoker, MBeanServer localNamespace) {
final Set<T> local = invoker.query(localNamespace, pattern, query);
// Add all matching MBeans from local namespace.
final Set<T> res = Util.cloneSet(local);
final boolean all = (pattern == null ||
pattern.getDomain().equals("*"));
if (pattern == null) pattern = ObjectName.WILDCARD;
final String domain = pattern.getDomain();
// If there's no domain pattern, just include the pattern's domain.
// Otherwiae, loop over all virtual domains (parent.getKeys()).
final String[] keys =
(pattern.isDomainPattern() ?
parent.getKeys() : new String[]{domain});
// Add all matching MBeans from each virtual domain
//
for (String key : keys) {
// Only invoke those virtual domain which are selected
// by the domain pattern
//
if (!all && !Util.isDomainSelected(key, domain))
continue;
try {
final MBeanServer mbs = parent.getInterceptor(key);
// mbs can be null if the interceptor was removed
// concurrently...
// See handlerMap and getKeys() in DispatchInterceptor
//
if (mbs == null) continue;
// If the domain is selected, we can replace the pattern
// by the actual domain. This is safer if we want to avoid
// a domain (which could be backed up by an MBeanServer) to
// return names from outside the domain.
// So instead of asking the domain handler for "foo" to
// return all names which match "?o*:type=Bla,*" we're
// going to ask it to return all names which match
// "foo:type=Bla,*"
//
final ObjectName subPattern = pattern.withDomain(key);
res.addAll(invoker.query(mbs, subPattern, query));
} catch (Exception x) {
LOG.finest("Ignoring exception " +
"when attempting to query namespace "+key+": "+x);
continue;
}
}
return res;
}
}
private final DefaultMBeanServerInterceptor localNamespace;
private final String mbeanServerName;
private final MBeanServerDelegate delegate;
/**
* Creates a DomainDispatchInterceptor with the specified
* repository instance.
*
* @param outer A pointer to the MBeanServer object that must be
* passed to the MBeans when invoking their
* {@link javax.management.MBeanRegistration} interface.
* @param delegate A pointer to the MBeanServerDelegate associated
* with the new MBeanServer. The new MBeanServer must register
* this MBean in its MBean repository.
* @param instantiator The MBeanInstantiator that will be used to
* instantiate MBeans and take care of class loading issues.
* @param repository The repository to use for this MBeanServer
*/
public DomainDispatchInterceptor(MBeanServer outer,
MBeanServerDelegate delegate,
MBeanInstantiator instantiator,
Repository repository,
NamespaceDispatchInterceptor namespaces) {
localNamespace = new DefaultMBeanServerInterceptor(outer,
delegate, instantiator,repository,namespaces);
mbeanServerName = Util.getMBeanServerSecurityName(delegate);
this.delegate = delegate;
}
final boolean isLocalHandlerNameFor(String domain,
ObjectName handlerName) {
if (domain == null) return true;
return handlerName.getDomain().equals(domain) &&
JMXDomain.TYPE_ASSIGNMENT.equals(
handlerName.getKeyPropertyListString());
}
@Override
void validateHandlerNameFor(String key, ObjectName name) {
super.validateHandlerNameFor(key,name);
final String[] domains = localNamespace.getDomains();
for (int i=0;i<domains.length;i++) {
if (domains[i].equals(key))
throw new IllegalArgumentException("domain "+key+
" is not empty");
}
}
@Override
final MBeanServer getInterceptorOrNullFor(ObjectName name) {
if (name == null) return localNamespace;
final String domain = name.getDomain();
if (domain.endsWith(NAMESPACE_SEPARATOR)) return localNamespace;
if (domain.contains(NAMESPACE_SEPARATOR)) return null;
final String localDomain = domain;
if (isLocalHandlerNameFor(localDomain,name)) {
LOG.finer("dispatching to local namespace");
return localNamespace;
}
final DomainInterceptor ns = getInterceptor(localDomain);
if (ns == null) {
if (LOG.isLoggable(Level.FINER)) {
LOG.finer("dispatching to local namespace: " + localDomain);
}
return getNextInterceptor();
}
if (LOG.isLoggable(Level.FINER)) {
LOG.finer("dispatching to domain: " + localDomain);
}
return ns;
}
private boolean multipleQuery(ObjectName pattern) {
if (pattern == null) return true;
if (pattern.isDomainPattern()) return true;
try {
// This is a bit of a hack. If there's any chance that a JMXDomain
// MBean name is selected by the given pattern then we must include
// the local namespace in our search.
// Returning true will have this effect.
if (pattern.apply(ALL_DOMAINS.withDomain(pattern.getDomain())))
return true;
} catch (MalformedObjectNameException x) {
// should not happen
throw new IllegalArgumentException(String.valueOf(pattern), x);
}
return false;
}
@Override
final QueryInterceptor getInterceptorForQuery(ObjectName pattern) {
// Check if we need to aggregate.
if (multipleQuery(pattern))
return new AggregatingQueryInterceptor(this);
// We don't need to aggregate: do the "simple" thing...
final String domain = pattern.getDomain();
// Do we have a virtual domain?
final DomainInterceptor ns = getInterceptor(domain);
if (ns != null) {
if (LOG.isLoggable(Level.FINER))
LOG.finer("dispatching to domain: " + domain);
return new QueryInterceptor(ns);
}
// We don't have a virtual domain. Send to local domains.
if (LOG.isLoggable(Level.FINER))
LOG.finer("dispatching to local namespace: " + domain);
return new QueryInterceptor(localNamespace);
}
@Override
final ObjectName getHandlerNameFor(String key)
throws MalformedObjectNameException {
return JMXDomain.getDomainObjectName(key);
}
@Override
final public String getHandlerKey(ObjectName name) {
return name.getDomain();
}
@Override
final DomainInterceptor createInterceptorFor(String key,
ObjectName name, JMXDomain handler,
Queue<Runnable> postRegisterQueue) {
final DomainInterceptor ns =
new DomainInterceptor(mbeanServerName,handler,key);
ns.addPostRegisterTask(postRegisterQueue, delegate);
if (LOG.isLoggable(Level.FINER)) {
LOG.finer("DomainInterceptor created: "+ns);
}
return ns;
}
@Override
final void interceptorReleased(DomainInterceptor interceptor,
Queue<Runnable> postDeregisterQueue) {
interceptor.addPostDeregisterTask(postDeregisterQueue, delegate);
}
@Override
final DefaultMBeanServerInterceptor getNextInterceptor() {
return localNamespace;
}
/**
* Returns the list of domains in which any MBean is currently
* registered.
*/
@Override
public String[] getDomains() {
// A JMXDomain is registered in its own domain.
// Therefore, localNamespace.getDomains() contains all domains.
// In addition, localNamespace will perform the necessary
// MBeanPermission checks for getDomains().
//
return localNamespace.getDomains();
}
/**
* Returns the number of MBeans registered in the MBean server.
*/
@Override
public Integer getMBeanCount() {
int count = getNextInterceptor().getMBeanCount().intValue();
final String[] keys = getKeys();
for (String key:keys) {
final MBeanServer mbs = getInterceptor(key);
if (mbs == null) continue;
count += mbs.getMBeanCount().intValue();
}
return Integer.valueOf(count);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright 2002-2005 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,35 +25,14 @@
package com.sun.jmx.interceptor;
import java.util.Set;
// RI import
import javax.management.DynamicMBean;
import javax.management.AttributeNotFoundException;
import javax.management.MBeanException;
import javax.management.ReflectionException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.QueryExp;
import javax.management.NotificationListener;
import javax.management.NotificationFilter;
import javax.management.ListenerNotFoundException;
import javax.management.IntrospectionException;
import javax.management.OperationsException;
import javax.management.MBeanNotificationInfo;
import javax.management.JMRuntimeException;
import java.io.ObjectInputStream;
import javax.management.InstanceNotFoundException;
import javax.management.NotCompliantMBeanException;
import javax.management.MBeanRegistrationException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.ObjectInstance;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.RuntimeOperationsException;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerDelegate;
import javax.management.OperationsException;
import javax.management.ReflectionException;
import javax.management.loading.ClassLoaderRepository;
/**
@ -85,618 +64,67 @@ import javax.management.loading.ClassLoaderRepository;
*
* @since 1.5
*/
public interface MBeanServerInterceptor extends MBeanServerConnection {
public interface MBeanServerInterceptor extends MBeanServer {
/**
* Instantiates and registers an MBean in the MBean server. The
* MBean server will use its {@link
* javax.management.loading.ClassLoaderRepository Default Loader
* Repository} to load the class of the MBean. An object name is
* associated to the MBean. If the object name given is null, the
* MBean must provide its own name by implementing the {@link
* javax.management.MBeanRegistration MBeanRegistration} interface
* and returning the name from the {@link
* javax.management.MBeanRegistration#preRegister preRegister} method.
*
* @param className The class name of the MBean to be instantiated.
* @param name The object name of the MBean. May be null.
* @param params An array containing the parameters of the
* constructor to be invoked.
* @param signature An array containing the signature of the
* constructor to be invoked.
*
* @return An <CODE>ObjectInstance</CODE>, containing the
* <CODE>ObjectName</CODE> and the Java class name of the newly
* instantiated MBean.
*
* @exception ReflectionException Wraps a
* <CODE>java.lang.ClassNotFoundException</CODE> or a
* <CODE>java.lang.Exception</CODE> that occurred when trying to
* invoke the MBean's constructor.
* @exception InstanceAlreadyExistsException The MBean is already
* under the control of the MBean server.
* @exception MBeanRegistrationException The
* <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
* @exception MBeanException The constructor of the MBean has
* thrown an exception
* @exception RuntimeOperationsException Wraps a
* <CODE>java.lang.IllegalArgumentException</CODE>: The className
* passed in parameter is null, the <CODE>ObjectName</CODE> passed
* in parameter contains a pattern or no <CODE>ObjectName</CODE>
* is specified for the MBean.
* This method should never be called.
* Usually hrows UnsupportedOperationException.
*/
public ObjectInstance createMBean(String className, ObjectName name,
Object params[], String signature[])
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException;
public Object instantiate(String className)
throws ReflectionException, MBeanException;
/**
* This method should never be called.
* Usually throws UnsupportedOperationException.
*/
public Object instantiate(String className, ObjectName loaderName)
throws ReflectionException, MBeanException,
InstanceNotFoundException;
/**
* This method should never be called.
* Usually throws UnsupportedOperationException.
*/
public Object instantiate(String className, Object[] params,
String[] signature) throws ReflectionException, MBeanException;
/**
* Instantiates and registers an MBean in the MBean server. The
* class loader to be used is identified by its object name. An
* object name is associated to the MBean. If the object name of
* the loader is not specified, the ClassLoader that loaded the
* MBean server will be used. If the MBean object name given is
* null, the MBean must provide its own name by implementing the
* {@link javax.management.MBeanRegistration MBeanRegistration}
* interface and returning the name from the {@link
* javax.management.MBeanRegistration#preRegister preRegister} method.
*
* @param className The class name of the MBean to be instantiated.
* @param name The object name of the MBean. May be null.
* @param params An array containing the parameters of the
* constructor to be invoked.
* @param signature An array containing the signature of the
* constructor to be invoked.
* @param loaderName The object name of the class loader to be used.
*
* @return An <CODE>ObjectInstance</CODE>, containing the
* <CODE>ObjectName</CODE> and the Java class name of the newly
* instantiated MBean.
*
* @exception ReflectionException Wraps a
* <CODE>java.lang.ClassNotFoundException</CODE> or a
* <CODE>java.lang.Exception</CODE> that occurred when trying to
* invoke the MBean's constructor.
* @exception InstanceAlreadyExistsException The MBean is already
* under the control of the MBean server.
* @exception MBeanRegistrationException The
* <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
* @exception MBeanException The constructor of the MBean has
* thrown an exception
* @exception InstanceNotFoundException The specified class loader
* is not registered in the MBean server.
* @exception RuntimeOperationsException Wraps a
* <CODE>java.lang.IllegalArgumentException</CODE>: The className
* passed in parameter is null, the <CODE>ObjectName</CODE> passed
* in parameter contains a pattern or no <CODE>ObjectName</CODE>
* is specified for the MBean.
*
* This method should never be called.
* Usually throws UnsupportedOperationException.
*/
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName, Object params[],
String signature[])
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException, InstanceNotFoundException;
public Object instantiate(String className, ObjectName loaderName,
Object[] params, String[] signature)
throws ReflectionException, MBeanException,
InstanceNotFoundException;
/**
* Registers a pre-existing object as an MBean with the MBean
* server. If the object name given is null, the MBean must
* provide its own name by implementing the {@link
* javax.management.MBeanRegistration MBeanRegistration} interface
* and returning the name from the {@link
* javax.management.MBeanRegistration#preRegister preRegister} method.
*
* @param object The MBean to be registered as an MBean.
* @param name The object name of the MBean. May be null.
*
* @return The <CODE>ObjectInstance</CODE> for the MBean that has
* been registered.
*
* @exception InstanceAlreadyExistsException The MBean is already
* under the control of the MBean server.
* @exception MBeanRegistrationException The
* <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
* @exception NotCompliantMBeanException This object is not a JMX
* compliant MBean
* @exception RuntimeOperationsException Wraps a
* <CODE>java.lang.IllegalArgumentException</CODE>: The object
* passed in parameter is null or no object name is specified.
* This method should never be called.
* Usually throws UnsupportedOperationException.
*/
public ObjectInstance registerMBean(Object object, ObjectName name)
throws InstanceAlreadyExistsException, MBeanRegistrationException,
NotCompliantMBeanException;
@Deprecated
public ObjectInputStream deserialize(ObjectName name, byte[] data)
throws InstanceNotFoundException, OperationsException;
/**
* Unregisters an MBean from the MBean server. The MBean is
* identified by its object name. Once the method has been
* invoked, the MBean may no longer be accessed by its object
* name.
*
* @param name The object name of the MBean to be unregistered.
*
* @exception InstanceNotFoundException The MBean specified is not
* registered in the MBean server.
* @exception MBeanRegistrationException The preDeregister
* ((<CODE>MBeanRegistration</CODE> interface) method of the MBean
* has thrown an exception.
* @exception RuntimeOperationsException Wraps a
* <CODE>java.lang.IllegalArgumentException</CODE>: The object
* name in parameter is null or the MBean you are when trying to
* unregister is the {@link javax.management.MBeanServerDelegate
* MBeanServerDelegate} MBean.
*
* This method should never be called.
* Usually throws UnsupportedOperationException.
*/
public void unregisterMBean(ObjectName name)
throws InstanceNotFoundException, MBeanRegistrationException;
@Deprecated
public ObjectInputStream deserialize(String className, byte[] data)
throws OperationsException, ReflectionException;
/**
* Gets the <CODE>ObjectInstance</CODE> for a given MBean
* registered with the MBean server.
*
* @param name The object name of the MBean.
*
* @return The <CODE>ObjectInstance</CODE> associated to the MBean
* specified by <VAR>name</VAR>.
*
* @exception InstanceNotFoundException The MBean specified is not
* registered in the MBean server.
* This method should never be called.
* Usually hrows UnsupportedOperationException.
*/
public ObjectInstance getObjectInstance(ObjectName name)
throws InstanceNotFoundException;
/**
* Gets MBeans controlled by the MBean server. This method allows
* any of the following to be obtained: All MBeans, a set of
* MBeans specified by pattern matching on the
* <CODE>ObjectName</CODE> and/or a Query expression, a specific
* MBean. When the object name is null or no domain and key
* properties are specified, all objects are to be selected (and
* filtered if a query is specified). It returns the set of
* <CODE>ObjectInstance</CODE> objects (containing the
* <CODE>ObjectName</CODE> and the Java Class name) for the
* selected MBeans.
*
* @param name The object name pattern identifying the MBeans to
* be retrieved. If null or no domain and key properties are
* specified, all the MBeans registered will be retrieved.
* @param query The query expression to be applied for selecting
* MBeans. If null no query expression will be applied for
* selecting MBeans.
*
* @return A set containing the <CODE>ObjectInstance</CODE>
* objects for the selected MBeans. If no MBean satisfies the
* query an empty list is returned.
*/
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query);
/**
* Gets the names of MBeans controlled by the MBean server. This
* method enables any of the following to be obtained: The names
* of all MBeans, the names of a set of MBeans specified by
* pattern matching on the <CODE>ObjectName</CODE> and/or a Query
* expression, a specific MBean name (equivalent to testing
* whether an MBean is registered). When the object name is null
* or no domain and key properties are specified, all objects are
* selected (and filtered if a query is specified). It returns the
* set of ObjectNames for the MBeans selected.
*
* @param name The object name pattern identifying the MBean names
* to be retrieved. If null oror no domain and key properties are
* specified, the name of all registered MBeans will be retrieved.
* @param query The query expression to be applied for selecting
* MBeans. If null no query expression will be applied for
* selecting MBeans.
*
* @return A set containing the ObjectNames for the MBeans
* selected. If no MBean satisfies the query, an empty list is
* returned.
*/
public Set<ObjectName> queryNames(ObjectName name, QueryExp query);
/**
* Checks whether an MBean, identified by its object name, is
* already registered with the MBean server.
*
* @param name The object name of the MBean to be checked.
*
* @return True if the MBean is already registered in the MBean
* server, false otherwise.
*
* @exception RuntimeOperationsException Wraps a
* <CODE>java.lang.IllegalArgumentException</CODE>: The object
* name in parameter is null.
*/
public boolean isRegistered(ObjectName name);
/**
* Returns the number of MBeans registered in the MBean server.
*/
public Integer getMBeanCount();
/**
* Gets the value of a specific attribute of a named MBean. The MBean
* is identified by its object name.
*
* @param name The object name of the MBean from which the
* attribute is to be retrieved.
* @param attribute A String specifying the name of the attribute
* to be retrieved.
*
* @return The value of the retrieved attribute.
*
* @exception AttributeNotFoundException The attribute specified
* is not accessible in the MBean.
* @exception MBeanException Wraps an exception thrown by the
* MBean's getter.
* @exception InstanceNotFoundException The MBean specified is not
* registered in the MBean server.
* @exception ReflectionException Wraps a
* <CODE>java.lang.Exception</CODE> thrown when trying to invoke
* the setter.
* @exception RuntimeOperationsException Wraps a
* <CODE>java.lang.IllegalArgumentException</CODE>: The object
* name in parameter is null or the attribute in parameter is
* null.
*/
public Object getAttribute(ObjectName name, String attribute)
throws MBeanException, AttributeNotFoundException,
InstanceNotFoundException, ReflectionException;
/**
* Enables the values of several attributes of a named MBean. The MBean
* is identified by its object name.
*
* @param name The object name of the MBean from which the
* attributes are retrieved.
* @param attributes A list of the attributes to be retrieved.
*
* @return The list of the retrieved attributes.
*
* @exception InstanceNotFoundException The MBean specified is not
* registered in the MBean server.
* @exception ReflectionException An exception occurred when
* trying to invoke the getAttributes method of a Dynamic MBean.
* @exception RuntimeOperationsException Wrap a
* <CODE>java.lang.IllegalArgumentException</CODE>: The object
* name in parameter is null or attributes in parameter is null.
*/
public AttributeList getAttributes(ObjectName name, String[] attributes)
throws InstanceNotFoundException, ReflectionException;
/**
* Sets the value of a specific attribute of a named MBean. The MBean
* is identified by its object name.
*
* @param name The name of the MBean within which the attribute is
* to be set.
* @param attribute The identification of the attribute to be set
* and the value it is to be set to.
*
* @exception InstanceNotFoundException The MBean specified is not
* registered in the MBean server.
* @exception AttributeNotFoundException The attribute specified
* is not accessible in the MBean.
* @exception InvalidAttributeValueException The value specified
* for the attribute is not valid.
* @exception MBeanException Wraps an exception thrown by the
* MBean's setter.
* @exception ReflectionException Wraps a
* <CODE>java.lang.Exception</CODE> thrown when trying to invoke
* the setter.
* @exception RuntimeOperationsException Wraps a
* <CODE>java.lang.IllegalArgumentException</CODE>: The object
* name in parameter is null or the attribute in parameter is
* null.
*/
public void setAttribute(ObjectName name, Attribute attribute)
throws InstanceNotFoundException, AttributeNotFoundException,
InvalidAttributeValueException, MBeanException,
ReflectionException;
/**
* Sets the values of several attributes of a named MBean. The MBean is
* identified by its object name.
*
* @param name The object name of the MBean within which the
* attributes are to be set.
* @param attributes A list of attributes: The identification of
* the attributes to be set and the values they are to be set to.
*
* @return The list of attributes that were set, with their new
* values.
*
* @exception InstanceNotFoundException The MBean specified is not
* registered in the MBean server.
* @exception ReflectionException An exception occurred when
* trying to invoke the getAttributes method of a Dynamic MBean.
* @exception RuntimeOperationsException Wraps a
* <CODE>java.lang.IllegalArgumentException</CODE>: The object
* name in parameter is null or attributes in parameter is null.
*/
public AttributeList setAttributes(ObjectName name,
AttributeList attributes)
throws InstanceNotFoundException, ReflectionException;
/**
* Invokes an operation on an MBean.
*
* @param name The object name of the MBean on which the method is
* to be invoked.
* @param operationName The name of the operation to be invoked.
* @param params An array containing the parameters to be set when
* the operation is invoked
* @param signature An array containing the signature of the
* operation. The class objects will be loaded using the same
* class loader as the one used for loading the MBean on which the
* operation was invoked.
*
* @return The object returned by the operation, which represents
* the result ofinvoking the operation on the MBean specified.
*
* @exception InstanceNotFoundException The MBean specified is not
* registered in the MBean server.
* @exception MBeanException Wraps an exception thrown by the
* MBean's invoked method.
* @exception ReflectionException Wraps a
* <CODE>java.lang.Exception</CODE> thrown while trying to invoke
* the method.
*/
public Object invoke(ObjectName name, String operationName,
Object params[], String signature[])
throws InstanceNotFoundException, MBeanException,
@Deprecated
public ObjectInputStream deserialize(String className,
ObjectName loaderName, byte[] data)
throws InstanceNotFoundException, OperationsException,
ReflectionException;
/**
* Returns the default domain used for naming the MBean.
* The default domain name is used as the domain part in the ObjectName
* of MBeans if no domain is specified by the user.
* This method should never be called.
* Usually throws UnsupportedOperationException.
*/
public String getDefaultDomain();
/**
* Returns the list of domains in which any MBean is currently
* registered.
*/
public String[] getDomains();
/**
* <p>Adds a listener to a registered MBean.</p>
*
* <P> A notification emitted by an MBean will be forwarded by the
* MBeanServer to the listener. If the source of the notification
* is a reference to an MBean object, the MBean server will replace it
* by that MBean's ObjectName. Otherwise the source is unchanged.
*
* @param name The name of the MBean on which the listener should
* be added.
* @param listener The listener object which will handle the
* notifications emitted by the registered MBean.
* @param filter The filter object. If filter is null, no
* filtering will be performed before handling notifications.
* @param handback The context to be sent to the listener when a
* notification is emitted.
*
* @exception InstanceNotFoundException The MBean name provided
* does not match any of the registered MBeans.
*/
public void addNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException;
/**
* <p>Adds a listener to a registered MBean.</p>
*
* <p>A notification emitted by an MBean will be forwarded by the
* MBeanServer to the listener. If the source of the notification
* is a reference to an MBean object, the MBean server will
* replace it by that MBean's ObjectName. Otherwise the source is
* unchanged.</p>
*
* <p>The listener object that receives notifications is the one
* that is registered with the given name at the time this method
* is called. Even if it is subsequently unregistered, it will
* continue to receive notifications.</p>
*
* @param name The name of the MBean on which the listener should
* be added.
* @param listener The object name of the listener which will
* handle the notifications emitted by the registered MBean.
* @param filter The filter object. If filter is null, no
* filtering will be performed before handling notifications.
* @param handback The context to be sent to the listener when a
* notification is emitted.
*
* @exception InstanceNotFoundException The MBean name of the
* notification listener or of the notification broadcaster does
* not match any of the registered MBeans.
* @exception RuntimeOperationsException Wraps an {@link
* IllegalArgumentException}. The MBean named by
* <code>listener</code> exists but does not implement the {@link
* NotificationListener} interface.
* @exception IOException A communication problem occurred when
* talking to the MBean server.
*/
public void addNotificationListener(ObjectName name,
ObjectName listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException;
/**
* Removes a listener from a registered MBean.
*
* <P> If the listener is registered more than once, perhaps with
* different filters or callbacks, this method will remove all
* those registrations.
*
* @param name The name of the MBean on which the listener should
* be removed.
* @param listener The object name of the listener to be removed.
*
* @exception InstanceNotFoundException The MBean name provided
* does not match any of the registered MBeans.
* @exception ListenerNotFoundException The listener is not
* registered in the MBean.
*/
public void removeNotificationListener(ObjectName name,
ObjectName listener)
throws InstanceNotFoundException, ListenerNotFoundException;
/**
* <p>Removes a listener from a registered MBean.</p>
*
* <p>The MBean must have a listener that exactly matches the
* given <code>listener</code>, <code>filter</code>, and
* <code>handback</code> parameters. If there is more than one
* such listener, only one is removed.</p>
*
* <p>The <code>filter</code> and <code>handback</code> parameters
* may be null if and only if they are null in a listener to be
* removed.</p>
*
* @param name The name of the MBean on which the listener should
* be removed.
* @param listener A listener that was previously added to this
* MBean.
* @param filter The filter that was specified when the listener
* was added.
* @param handback The handback that was specified when the
* listener was added.
*
* @exception InstanceNotFoundException The MBean name provided
* does not match any of the registered MBeans.
* @exception ListenerNotFoundException The listener is not
* registered in the MBean, or it is not registered with the given
* filter and handback.
*/
public void removeNotificationListener(ObjectName name,
ObjectName listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException, ListenerNotFoundException;
/**
* <p>Removes a listener from a registered MBean.</p>
*
* <P> If the listener is registered more than once, perhaps with
* different filters or callbacks, this method will remove all
* those registrations.
*
* @param name The name of the MBean on which the listener should
* be removed.
* @param listener The listener object which will handle the
* notifications emitted by the registered MBean.
*
* @exception InstanceNotFoundException The MBean name provided
* does not match any of the registered MBeans.
* @exception ListenerNotFoundException The listener is not
* registered in the MBean.
*/
public void removeNotificationListener(ObjectName name,
NotificationListener listener)
throws InstanceNotFoundException, ListenerNotFoundException;
/**
* <p>Removes a listener from a registered MBean.</p>
*
* <p>The MBean must have a listener that exactly matches the
* given <code>listener</code>, <code>filter</code>, and
* <code>handback</code> parameters. If there is more than one
* such listener, only one is removed.</p>
*
* <p>The <code>filter</code> and <code>handback</code> parameters
* may be null if and only if they are null in a listener to be
* removed.</p>
*
* @param name The name of the MBean on which the listener should
* be removed.
* @param listener A listener that was previously added to this
* MBean.
* @param filter The filter that was specified when the listener
* was added.
* @param handback The handback that was specified when the
* listener was added.
*
* @exception InstanceNotFoundException The MBean name provided
* does not match any of the registered MBeans.
* @exception ListenerNotFoundException The listener is not
* registered in the MBean, or it is not registered with the given
* filter and handback.
*/
public void removeNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException, ListenerNotFoundException;
/**
* This method discovers the attributes and operations that an
* MBean exposes for management.
*
* @param name The name of the MBean to analyze
*
* @return An instance of <CODE>MBeanInfo</CODE> allowing the
* retrieval of all attributes and operations of this MBean.
*
* @exception IntrospectionException An exception occurred during
* introspection.
* @exception InstanceNotFoundException The MBean specified was
* not found.
* @exception ReflectionException An exception occurred when
* trying to invoke the getMBeanInfo of a Dynamic MBean.
*/
public MBeanInfo getMBeanInfo(ObjectName name)
throws InstanceNotFoundException, IntrospectionException,
ReflectionException;
/**
* Returns true if the MBean specified is an instance of the
* specified class, false otherwise.
*
* @param name The <CODE>ObjectName</CODE> of the MBean.
* @param className The name of the class.
*
* @return true if the MBean specified is an instance of the
* specified class, false otherwise.
*
* @exception InstanceNotFoundException The MBean specified is not
* registered in the MBean server.
*/
public boolean isInstanceOf(ObjectName name, String className)
throws InstanceNotFoundException;
/**
* <p>Return the {@link java.lang.ClassLoader} that was used for
* loading the class of the named MBean.
* @param mbeanName The ObjectName of the MBean.
* @return The ClassLoader used for that MBean.
* @exception InstanceNotFoundException if the named MBean is not found.
*/
public ClassLoader getClassLoaderFor(ObjectName mbeanName)
throws InstanceNotFoundException;
/**
* <p>Return the named {@link java.lang.ClassLoader}.
* @param loaderName The ObjectName of the ClassLoader.
* @return The named ClassLoader.
* @exception InstanceNotFoundException if the named ClassLoader is
* not found.
*/
public ClassLoader getClassLoader(ObjectName loaderName)
throws InstanceNotFoundException;
public ClassLoaderRepository getClassLoaderRepository();
}

View file

@ -0,0 +1,127 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.interceptor;
import java.io.ObjectInputStream;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.ObjectName;
import javax.management.OperationsException;
import javax.management.ReflectionException;
import javax.management.loading.ClassLoaderRepository;
/**
* An abstract class for MBeanServerInterceptorSupport.
* Some methods in MBeanServerInterceptor should never be called.
* This base class provides an implementation of these methods that simply
* throw an {@link UnsupportedOperationException}.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
public abstract class MBeanServerInterceptorSupport
implements MBeanServerInterceptor {
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
public Object instantiate(String className)
throws ReflectionException, MBeanException {
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
public Object instantiate(String className, ObjectName loaderName)
throws ReflectionException, MBeanException,
InstanceNotFoundException {
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
public Object instantiate(String className, Object[] params,
String[] signature) throws ReflectionException, MBeanException {
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
public Object instantiate(String className, ObjectName loaderName,
Object[] params, String[] signature)
throws ReflectionException, MBeanException,
InstanceNotFoundException {
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
@Deprecated
public ObjectInputStream deserialize(ObjectName name, byte[] data)
throws InstanceNotFoundException, OperationsException {
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
@Deprecated
public ObjectInputStream deserialize(String className, byte[] data)
throws OperationsException, ReflectionException {
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
@Deprecated
public ObjectInputStream deserialize(String className,
ObjectName loaderName, byte[] data)
throws InstanceNotFoundException, OperationsException,
ReflectionException {
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
public ClassLoaderRepository getClassLoaderRepository() {
throw new UnsupportedOperationException("Not applicable.");
}
}

View file

@ -0,0 +1,236 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.interceptor;
import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.mbeanserver.MBeanInstantiator;
import com.sun.jmx.mbeanserver.Repository;
import com.sun.jmx.mbeanserver.Util;
import com.sun.jmx.namespace.NamespaceInterceptor;
import java.util.Queue;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.MBeanServer;
import javax.management.MBeanServerDelegate;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.namespace.JMXDomain;
import javax.management.namespace.JMXNamespace;
import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
/**
* A dispatcher that dispatches to NamespaceInterceptors.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
public class NamespaceDispatchInterceptor
extends DispatchInterceptor<NamespaceInterceptor, JMXNamespace> {
/**
* A logger for this class.
**/
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
private static final int NAMESPACE_SEPARATOR_LENGTH =
NAMESPACE_SEPARATOR.length();
private final DomainDispatchInterceptor localNamespace;
private final String serverName;
/**
* Creates a NamespaceDispatchInterceptor with the specified
* repository instance.
* <p>Do not forget to call <code>initialize(outer,delegate)</code>
* before using this object.
*
* @param outer A pointer to the MBeanServer object that must be
* passed to the MBeans when invoking their
* {@link javax.management.MBeanRegistration} interface.
* @param delegate A pointer to the MBeanServerDelegate associated
* with the new MBeanServer. The new MBeanServer must register
* this MBean in its MBean repository.
* @param instantiator The MBeanInstantiator that will be used to
* instantiate MBeans and take care of class loading issues.
* @param repository The repository to use for this MBeanServer
*/
public NamespaceDispatchInterceptor(MBeanServer outer,
MBeanServerDelegate delegate,
MBeanInstantiator instantiator,
Repository repository) {
localNamespace = new DomainDispatchInterceptor(outer,delegate,
instantiator,repository,this);
serverName = Util.getMBeanServerSecurityName(delegate);
}
// TODO: Should move that to JMXNamespace? or to ObjectName?
/**
* Get first name space in ObjectName path. Ignore leading namespace
* separators.
**/
public static String getFirstNamespace(ObjectName name) {
if (name == null) return "";
final String domain = name.getDomain();
if (domain.equals("")) return "";
int first = 0;
int end = domain.indexOf(NAMESPACE_SEPARATOR,first);
while (end == first) {
first = end+NAMESPACE_SEPARATOR_LENGTH;
end = domain.indexOf(NAMESPACE_SEPARATOR,first);
if (end == -1) break;
}
if (end == -1) return "";
final String namespace = domain.substring(first,end);
return namespace;
}
/**
* Called by the DefaultMBeanServerInterceptor, just before adding an
* MBean to the repository.
*
* @param resource the MBean to be registered.
* @param logicalName the name of the MBean to be registered.
*/
final void checkLocallyRegistrable(Object resource,
ObjectName logicalName) {
if (!(resource instanceof JMXNamespace) &&
logicalName.getDomain().contains(NAMESPACE_SEPARATOR))
throw new IllegalArgumentException(String.valueOf(logicalName)+
": Invalid ObjectName for an instance of " +
resource.getClass().getName());
}
final boolean isLocalHandlerNameFor(String namespace,
ObjectName handlerName) {
return handlerName.getDomain().equals(namespace+NAMESPACE_SEPARATOR) &&
JMXNamespace.TYPE_ASSIGNMENT.equals(
handlerName.getKeyPropertyListString());
}
@Override
final MBeanServer getInterceptorOrNullFor(ObjectName name) {
final String namespace = getFirstNamespace(name);
if (namespace.equals("") || isLocalHandlerNameFor(namespace,name) ||
name.getDomain().equals(namespace+NAMESPACE_SEPARATOR)) {
LOG.finer("dispatching to local name space");
return localNamespace;
}
final NamespaceInterceptor ns = getInterceptor(namespace);
if (LOG.isLoggable(Level.FINER)) {
if (ns != null) {
LOG.finer("dispatching to name space: " + namespace);
} else {
LOG.finer("no handler for: " + namespace);
}
}
return ns;
}
@Override
final QueryInterceptor getInterceptorForQuery(ObjectName pattern) {
final String namespace = getFirstNamespace(pattern);
if (namespace.equals("") || isLocalHandlerNameFor(namespace,pattern) ||
pattern.getDomain().equals(namespace+NAMESPACE_SEPARATOR)) {
LOG.finer("dispatching to local name space");
return new QueryInterceptor(localNamespace);
}
final NamespaceInterceptor ns = getInterceptor(namespace);
if (LOG.isLoggable(Level.FINER)) {
if (ns != null) {
LOG.finer("dispatching to name space: " + namespace);
} else {
LOG.finer("no handler for: " + namespace);
}
}
if (ns == null) return null;
return new QueryInterceptor(ns);
}
@Override
final ObjectName getHandlerNameFor(String key)
throws MalformedObjectNameException {
return ObjectName.getInstance(key+NAMESPACE_SEPARATOR,
"type", JMXNamespace.TYPE);
}
@Override
final public String getHandlerKey(ObjectName name) {
return getFirstNamespace(name);
}
@Override
final NamespaceInterceptor createInterceptorFor(String key,
ObjectName name, JMXNamespace handler,
Queue<Runnable> postRegisterQueue) {
final NamespaceInterceptor ns =
new NamespaceInterceptor(serverName,handler,key);
if (LOG.isLoggable(Level.FINER)) {
LOG.finer("NamespaceInterceptor created: "+ns);
}
return ns;
}
@Override
final DomainDispatchInterceptor getNextInterceptor() {
return localNamespace;
}
/**
* Returns the list of domains in which any MBean is currently
* registered.
*/
@Override
public String[] getDomains() {
return localNamespace.getDomains();
}
@Override
public void addNamespace(ObjectName name, JMXNamespace handler,
Queue<Runnable> postRegisterQueue) {
if (handler instanceof JMXDomain)
localNamespace.addNamespace(name,
(JMXDomain)handler,postRegisterQueue);
else super.addNamespace(name,handler,postRegisterQueue);
}
@Override
public void removeNamespace(ObjectName name, JMXNamespace handler,
Queue<Runnable> postDeregisterQueue) {
if (handler instanceof JMXDomain)
localNamespace.removeNamespace(name,(JMXDomain)handler,
postDeregisterQueue);
else super.removeNamespace(name,handler,postDeregisterQueue);
}
}

View file

@ -51,6 +51,8 @@ import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.management.namespace.JMXNamespaces;
import javax.management.namespace.MBeanServerSupport;
import javax.management.remote.IdentityMBeanServerForwarder;
public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
@ -285,14 +287,14 @@ public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
if (!pattern.apply(mbeanName))
return false;
// final String dompat = pattern.getDomain();
// if (!dompat.contains(JMXNamespaces.NAMESPACE_SEPARATOR))
// return true; // We already checked that patterns apply.
//
// if (mbeanName.getDomain().endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) {
// // only matches if pattern ends with //
// return dompat.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR);
// }
final String dompat = pattern.getDomain();
if (!dompat.contains(JMXNamespaces.NAMESPACE_SEPARATOR))
return true; // We already checked that patterns apply.
if (mbeanName.getDomain().endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) {
// only matches if pattern ends with //
return dompat.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR);
}
// should not come here, unless mbeanName contains a // in the
// middle of its domain, which would be weird.

View file

@ -1,5 +1,5 @@
/*
* Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 1999-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,44 +25,42 @@
package com.sun.jmx.mbeanserver;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.Set;
import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
import com.sun.jmx.interceptor.NamespaceDispatchInterceptor;
import java.io.ObjectInputStream;
import java.security.AccessController;
import java.security.Permission;
import java.security.PrivilegedExceptionAction;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Level;
// RI import
import javax.management.MBeanPermission;
import javax.management.AttributeNotFoundException;
import javax.management.MBeanException;
import javax.management.ReflectionException;
import javax.management.MBeanInfo;
import javax.management.QueryExp;
import javax.management.NotificationListener;
import javax.management.NotificationFilter;
import javax.management.ListenerNotFoundException;
import javax.management.IntrospectionException;
import javax.management.OperationsException;
import javax.management.InstanceNotFoundException;
import javax.management.NotCompliantMBeanException;
import javax.management.MBeanRegistrationException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InvalidAttributeValueException;
import javax.management.ObjectName;
import javax.management.ObjectInstance;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.RuntimeOperationsException;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanPermission;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MBeanServerDelegate;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.OperationsException;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.management.RuntimeOperationsException;
import javax.management.loading.ClassLoaderRepository;
import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
import com.sun.jmx.interceptor.DefaultMBeanServerInterceptor;
import com.sun.jmx.interceptor.MBeanServerInterceptor;
/**
* This is the base class for MBean manipulation on the agent side. It
* contains the methods necessary for the creation, registration, and
@ -102,15 +100,14 @@ public final class JmxMBeanServer
/** true if interceptors are enabled **/
private final boolean interceptorsEnabled;
/** Revisit: transient ??? **/
private final transient MBeanServer outerShell;
private final MBeanServer outerShell;
/** Revisit: transient ??? **/
private transient MBeanServerInterceptor mbsInterceptor = null;
private volatile MBeanServer mbsInterceptor = null;
/** Revisit: transient ??? **/
/** The MBeanServerDelegate object representing the MBean Server */
private final transient MBeanServerDelegate mBeanServerDelegateObject;
private final MBeanServerDelegate mBeanServerDelegateObject;
private final String mbeanServerName;
/**
* <b>Package:</b> Creates an MBeanServer with the
@ -243,9 +240,10 @@ public final class JmxMBeanServer
final Repository repository = new Repository(domain,fairLock);
this.mbsInterceptor =
new DefaultMBeanServerInterceptor(outer, delegate, instantiator,
new NamespaceDispatchInterceptor(outer, delegate, instantiator,
repository);
this.interceptorsEnabled = interceptors;
this.mbeanServerName = Util.getMBeanServerSecurityName(delegate);
initialize();
}
@ -941,7 +939,8 @@ public final class JmxMBeanServer
throws ReflectionException, MBeanException {
/* Permission check */
checkMBeanPermission(className, null, null, "instantiate");
checkMBeanPermission(mbeanServerName, className, null, null,
"instantiate");
return instantiator.instantiate(className);
}
@ -978,7 +977,8 @@ public final class JmxMBeanServer
InstanceNotFoundException {
/* Permission check */
checkMBeanPermission(className, null, null, "instantiate");
checkMBeanPermission(mbeanServerName, className, null,
null, "instantiate");
ClassLoader myLoader = outerShell.getClass().getClassLoader();
return instantiator.instantiate(className, loaderName, myLoader);
@ -1016,7 +1016,8 @@ public final class JmxMBeanServer
throws ReflectionException, MBeanException {
/* Permission check */
checkMBeanPermission(className, null, null, "instantiate");
checkMBeanPermission(mbeanServerName, className, null, null,
"instantiate");
ClassLoader myLoader = outerShell.getClass().getClassLoader();
return instantiator.instantiate(className, params, signature,
@ -1059,7 +1060,8 @@ public final class JmxMBeanServer
InstanceNotFoundException {
/* Permission check */
checkMBeanPermission(className, null, null, "instantiate");
checkMBeanPermission(mbeanServerName, className, null,
null, "instantiate");
ClassLoader myLoader = outerShell.getClass().getClassLoader();
return instantiator.instantiate(className,loaderName,params,signature,
@ -1236,7 +1238,7 @@ public final class JmxMBeanServer
"Unexpected exception occurred", e);
}
throw new
IllegalStateException("Can't register delegate.");
IllegalStateException("Can't register delegate.",e);
}
@ -1278,7 +1280,7 @@ public final class JmxMBeanServer
* are not enabled on this object.
* @see #interceptorsEnabled
**/
public synchronized MBeanServerInterceptor getMBeanServerInterceptor() {
public synchronized MBeanServer getMBeanServerInterceptor() {
if (interceptorsEnabled) return mbsInterceptor;
else throw new UnsupportedOperationException(
"MBeanServerInterceptors are disabled.");
@ -1292,7 +1294,7 @@ public final class JmxMBeanServer
* @see #interceptorsEnabled
**/
public synchronized void
setMBeanServerInterceptor(MBeanServerInterceptor interceptor) {
setMBeanServerInterceptor(MBeanServer interceptor) {
if (!interceptorsEnabled) throw new UnsupportedOperationException(
"MBeanServerInterceptors are disabled.");
if (interceptor == null) throw new
@ -1330,7 +1332,8 @@ public final class JmxMBeanServer
**/
public ClassLoaderRepository getClassLoaderRepository() {
/* Permission check */
checkMBeanPermission(null, null, null, "getClassLoaderRepository");
checkMBeanPermission(mbeanServerName, null, null,
null, "getClassLoaderRepository");
return secureClr;
}
@ -1484,14 +1487,16 @@ public final class JmxMBeanServer
// SECURITY CHECKS
//----------------
private static void checkMBeanPermission(String classname,
private static void checkMBeanPermission(String serverName,
String classname,
String member,
ObjectName objectName,
String actions)
throws SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
Permission perm = new MBeanPermission(classname,
Permission perm = new MBeanPermission(serverName,
classname,
member,
objectName,
actions);

View file

@ -224,7 +224,7 @@ public abstract class MXBeanLookup {
throws InvalidObjectException {
String domain = prefix + name.getDomain();
try {
name = switchDomain(domain, name);
name = name.withDomain(domain);
} catch (MalformedObjectNameException e) {
throw EnvHelp.initCause(
new InvalidObjectException(e.getMessage()), e);
@ -242,7 +242,7 @@ public abstract class MXBeanLookup {
"Proxy's name does not start with " + prefix + ": " + name);
}
try {
name = switchDomain(domain.substring(prefix.length()), name);
name = name.withDomain(domain.substring(prefix.length()));
} catch (MalformedObjectNameException e) {
throw EnvHelp.initCause(new OpenDataException(e.getMessage()), e);
}
@ -269,14 +269,6 @@ public abstract class MXBeanLookup {
currentLookup.set(lookup);
}
// Method temporarily added until we have ObjectName.switchDomain in the
// public API. Note that this method DOES NOT PRESERVE the order of
// keys in the ObjectName so it must not be used in the final release.
static ObjectName switchDomain(String domain, ObjectName name)
throws MalformedObjectNameException {
return new ObjectName(domain, name.getKeyPropertyList());
}
private static final ThreadLocal<MXBeanLookup> currentLookup =
new ThreadLocal<MXBeanLookup>();

View file

@ -45,7 +45,6 @@ import javax.management.QueryExp;
import javax.management.RuntimeOperationsException;
/**
* The RepositorySupport implements the Repository interface.
* This repository does not support persistency.
*
* @since 1.5
@ -197,9 +196,9 @@ public class Repository {
if (isPropertyValuePattern &&
pattern.isPropertyValuePattern(keys[i])) {
// wildmatch key property values
final char[] val_pattern = values[i].toCharArray();
final char[] val_string = v.toCharArray();
if (wildmatch(val_string,val_pattern))
// values[i] is the pattern;
// v is the string
if (Util.wildmatch(v,values[i]))
continue;
else
return false;
@ -236,86 +235,6 @@ public class Repository {
}
}
/** Match a string against a shell-style pattern. The only pattern
characters recognised are <code>?</code>, standing for any one
character, and <code>*</code>, standing for any string of
characters, including the empty string.
@param str the string to match, as a character array.
@param pat the pattern to match the string against, as a
character array.
@return true if and only if the string matches the pattern.
*/
/* The algorithm is a classical one. We advance pointers in
parallel through str and pat. If we encounter a star in pat,
we remember its position and continue advancing. If at any
stage we get a mismatch between str and pat, we look to see if
there is a remembered star. If not, we fail. If so, we
retreat pat to just past that star and str to the position
after the last one we tried, and we let the match advance
again.
Even though there is only one remembered star position, the
algorithm works when there are several stars in the pattern.
When we encounter the second star, we forget the first one.
This is OK, because if we get to the second star in A*B*C
(where A etc are arbitrary strings), we have already seen AXB.
We're therefore setting up a match of *C against the remainder
of the string, which will match if that remainder looks like
YC, so the whole string looks like AXBYC.
*/
public static boolean wildmatch(char[] str, char[] pat) {
int stri; // index in str
int pati; // index in pat
int starstri; // index for backtrack if "*" attempt fails
int starpati; // index for backtrack if "*" attempt fails, +1
final int strlen = str.length;
final int patlen = pat.length;
stri = pati = 0;
starstri = starpati = -1;
/* On each pass through this loop, we either advance pati,
or we backtrack pati and advance starstri. Since starstri
is only ever assigned from pati, the loop must terminate. */
while (true) {
if (pati < patlen) {
final char patc = pat[pati];
switch (patc) {
case '?':
if (stri == strlen)
break;
stri++;
pati++;
continue;
case '*':
pati++;
starpati = pati;
starstri = stri;
continue;
default:
if (stri < strlen && str[stri] == patc) {
stri++;
pati++;
continue;
}
break;
}
} else if (stri == strlen)
return true;
// Mismatched, can we backtrack to a "*"?
if (starpati < 0 || starstri == strlen)
return false;
// Retry the match one position later in str
pati = starpati;
starstri++;
stri = starstri;
}
}
private void addNewDomMoi(final DynamicMBean object,
final String dom,
final ObjectName name,
@ -480,7 +399,7 @@ public class Repository {
name = Util.newObjectName(domain + name.toString());
// Do we have default domain ?
if (dom == domain) {
if (dom == domain) { // ES: OK (dom & domain are interned)
to_default_domain = true;
dom = domain;
} else {
@ -652,10 +571,9 @@ public class Repository {
}
// Pattern matching in the domain name (*, ?)
char[] dom2Match = name.getDomain().toCharArray();
final String dom2Match = name.getDomain();
for (String dom : domainTb.keySet()) {
char[] theDom = dom.toCharArray();
if (wildmatch(theDom, dom2Match)) {
if (Util.wildpathmatch(dom, dom2Match)) {
final Map<String,NamedObject> moiTb = domainTb.get(dom);
if (allNames)
result.addAll(moiTb.values());
@ -726,7 +644,7 @@ public class Repository {
// need to reinstantiate a hashtable because of possible
// big buckets array size inside table, never cleared,
// thus the new !
if (dom == domain)
if (dom == domain) // ES: OK dom and domain are interned.
domainTb.put(domain, new HashMap<String,NamedObject>());
}

View file

@ -28,17 +28,16 @@ package com.sun.jmx.mbeanserver;
import javax.management.MBeanServer;
import javax.management.MBeanServerDelegate;
import com.sun.jmx.interceptor.MBeanServerInterceptor;
/**
* Extends the MBeanServer and MBeanServerInterceptor interface to
* Extends the MBeanServer interface to
* provide methods for getting the MetaData and MBeanServerInstantiator
* objects associated with an MBeanServer.
*
* @since 1.5
*/
public interface SunJmxMBeanServer
extends MBeanServerInterceptor, MBeanServer {
extends MBeanServer {
/**
* Return the MBeanInstantiator associated to this MBeanServer.
@ -68,7 +67,7 @@ public interface SunJmxMBeanServer
* are not enabled on this object.
* @see #interceptorsEnabled
**/
public MBeanServerInterceptor getMBeanServerInterceptor();
public MBeanServer getMBeanServerInterceptor();
/**
* Set the MBeanServerInterceptor.
@ -77,7 +76,7 @@ public interface SunJmxMBeanServer
* are not enabled on this object.
* @see #interceptorsEnabled
**/
public void setMBeanServerInterceptor(MBeanServerInterceptor interceptor);
public void setMBeanServerInterceptor(MBeanServer interceptor);
/**
* <p>Return the MBeanServerDelegate representing the MBeanServer.

View file

@ -25,6 +25,8 @@
package com.sun.jmx.mbeanserver;
import com.sun.jmx.defaults.JmxProperties;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -42,11 +44,22 @@ import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.WeakHashMap;
import java.util.logging.Level;
import javax.management.MBeanServer;
import javax.management.MBeanServerDelegate;
import javax.management.MBeanServerFactory;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.loading.ClassLoaderRepository;
import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
public class Util {
private final static int NAMESPACE_SEPARATOR_LENGTH =
NAMESPACE_SEPARATOR.length();
public final static String ILLEGAL_MBEANSERVER_NAME_CHARS=";:*?";
static <K, V> Map<K, V> newMap() {
return new HashMap<K, V>();
}
@ -145,6 +158,270 @@ public class Util {
return hash;
}
/** Match a part of a string against a shell-style pattern.
The only pattern characters recognized are <code>?</code>,
standing for any one character,
and <code>*</code>, standing for any string of
characters, including the empty string. For instance,
{@code wildmatch("sandwich","sa?d*ch",1,4,1,4)} will match
{@code "and"} against {@code "a?d"}.
@param str the string containing the sequence to match.
@param pat a string containing a pattern to match the sub string
against.
@param stri the index in the string at which matching should begin.
@param strend the index in the string at which the matching should
end.
@param pati the index in the pattern at which matching should begin.
@param patend the index in the pattern at which the matching should
end.
@return true if and only if the string matches the pattern.
*/
/* The algorithm is a classical one. We advance pointers in
parallel through str and pat. If we encounter a star in pat,
we remember its position and continue advancing. If at any
stage we get a mismatch between str and pat, we look to see if
there is a remembered star. If not, we fail. If so, we
retreat pat to just past that star and str to the position
after the last one we tried, and we let the match advance
again.
Even though there is only one remembered star position, the
algorithm works when there are several stars in the pattern.
When we encounter the second star, we forget the first one.
This is OK, because if we get to the second star in A*B*C
(where A etc are arbitrary strings), we have already seen AXB.
We're therefore setting up a match of *C against the remainder
of the string, which will match if that remainder looks like
YC, so the whole string looks like AXBYC.
*/
private static boolean wildmatch(final String str, final String pat,
int stri, final int strend, int pati, final int patend) {
// System.out.println("matching "+pat.substring(pati,patend)+
// " against "+str.substring(stri, strend));
int starstri; // index for backtrack if "*" attempt fails
int starpati; // index for backtrack if "*" attempt fails, +1
starstri = starpati = -1;
/* On each pass through this loop, we either advance pati,
or we backtrack pati and advance starstri. Since starstri
is only ever assigned from pati, the loop must terminate. */
while (true) {
if (pati < patend) {
final char patc = pat.charAt(pati);
switch (patc) {
case '?':
if (stri == strend)
break;
stri++;
pati++;
continue;
case '*':
pati++;
starpati = pati;
starstri = stri;
continue;
default:
if (stri < strend && str.charAt(stri) == patc) {
stri++;
pati++;
continue;
}
break;
}
} else if (stri == strend)
return true;
// Mismatched, can we backtrack to a "*"?
if (starpati < 0 || starstri == strend)
return false;
// Retry the match one position later in str
pati = starpati;
starstri++;
stri = starstri;
}
}
/** Match a string against a shell-style pattern. The only pattern
characters recognized are <code>?</code>, standing for any one
character, and <code>*</code>, standing for any string of
characters, including the empty string.
@param str the string to match.
@param pat the pattern to match the string against.
@return true if and only if the string matches the pattern.
*/
public static boolean wildmatch(String str, String pat) {
return wildmatch(str,pat,0,str.length(),0,pat.length());
}
/**
* Matches a string against a pattern, as a name space path.
* This is a special matching where * and ?? don't match //.
* The string is split in sub-strings separated by //, and the
* pattern is split in sub-patterns separated by //. Each sub-string
* is matched against its corresponding sub-pattern.
* so <elt-1>//<elt2>//...//<elt-n> matches <pat-1>//<pat-2>//...//<pat-q>
* only if n==q and for ( i = 1 => n) elt-i matches pat-i.
*
* In addition, if we encounter a pattern element which is exactly
* **, it can match any number of path-elements - but it must match at
* least one element.
* When we encounter such a meta-wildcard, we remember its position
* and the position in the string path, and we advance both the pattern
* and the string. Later, if we encounter a mismatch in pattern & string,
* we rewind the position in pattern to just after the meta-wildcard,
* and we backtrack the string to i+1 element after the position
* we had when we first encountered the meta-wildcard, i being the
* position when we last backtracked the string.
*
* The backtracking logic is an adaptation of the logic in wildmatch
* above.
* See test/javax/mangement/ObjectName/ApplyWildcardTest.java
*
* Note: this thing is called 'wild' - and that's for a reason ;-)
**/
public static boolean wildpathmatch(String str, String pat) {
final int strlen = str.length();
final int patlen = pat.length();
int stri = 0;
int pati = 0;
int starstri; // index for backtrack if "**" attempt fails
int starpati; // index for backtrack if "**" attempt fails
starstri = starpati = -1;
while (true) {
// System.out.println("pati="+pati+", stri="+stri);
final int strend = str.indexOf(NAMESPACE_SEPARATOR, stri);
final int patend = pat.indexOf(NAMESPACE_SEPARATOR, pati);
// no // remaining in either string or pattern: simple wildmatch
// until end of string.
if (strend == -1 && patend == -1) {
// System.out.println("last sub pattern, last sub element...");
// System.out.println("wildmatch("+str.substring(stri,strlen)+
// ","+pat.substring(pati,patlen)+")");
return wildmatch(str,pat,stri,strlen,pati,patlen);
}
// no // remaining in string, but at least one remaining in
// pattern
// => no match
if (strend == -1) {
// System.out.println("pattern has more // than string...");
return false;
}
// strend is != -1, but patend might.
// detect wildcard **
if (patend == pati+2 && pat.charAt(pati)=='*' &&
pat.charAt(pati+1)=='*') {
// if we reach here we know that neither strend nor patend are
// equals to -1.
stri = strend + NAMESPACE_SEPARATOR_LENGTH;
pati = patend + NAMESPACE_SEPARATOR_LENGTH;
starpati = pati; // position just after **// in pattern
starstri = stri; // we eat 1 element in string, and remember
// the position for backtracking and eating
// one more element if needed.
// System.out.println("starpati="+pati);
continue;
}
// This is a bit hacky: * can match // when // is at the end
// of the string, so we include the // delimiter in the pattern
// matching. Either we're in the middle of the path, so including
// // both at the end of the pattern and at the end of the string
// has no effect - match(*//,dfsd//) is equivalent to match(*,dfsd)
// or we're at the end of the pattern path, in which case
// including // at the end of the string will have the desired
// effect (provided that we detect the end of matching correctly,
// see further on).
//
final int endpat =
((patend > -1)?patend+NAMESPACE_SEPARATOR_LENGTH:patlen);
final int endstr =
((strend > -1)?strend+NAMESPACE_SEPARATOR_LENGTH:strlen);
// if we reach the end of the pattern, or if elt-i & pat-i
// don't match, we have a mismatch.
// Note: we know that strend != -1, therefore patend==-1
// indicates a mismatch unless pattern can match
// a // at the end, and strend+2=strlen.
// System.out.println("wildmatch("+str.substring(stri,endstr)+","+
// pat.substring(pati,endpat)+")");
if (!wildmatch(str,pat,stri,endstr,pati,endpat)) {
// System.out.println("nomatch");
// if we have a mismatch and didn't encounter any meta-wildcard,
// we return false. String & pattern don't match.
if (starpati < 0) return false;
// If we reach here, we had a meta-wildcard.
// We need to backtrack to the wildcard, and make it eat an
// additional string element.
//
stri = str.indexOf(NAMESPACE_SEPARATOR, starstri);
// System.out.println("eating one additional element? "+stri);
// If there's no more elements to eat, string and pattern
// don't match => return false.
if (stri == -1) return false;
// Backtrack to where we were when we last matched against
// the meta-wildcard, make it eat an additional path element,
// remember the new positions, and continue from there...
//
stri = stri + NAMESPACE_SEPARATOR_LENGTH;
starstri = stri;
pati = starpati;
// System.out.println("skiping to stri="+stri);
continue;
}
// Here we know that strend > -1 but we can have patend == -1.
//
// So if we reach here, we know pat-i+//? has matched
// elt-i+//
//
// If patend==-1, we know that there was no delimiter
// at the end of the pattern, that we are at the last pattern,
// and therefore that pat-i has matched elt-i+//
//
// In that case we can consider that we have a match only if
// elt-i is also the last path element in the string, which is
// equivalent to saying that strend+2==strlen.
//
if (patend == -1 && starpati == -1)
return (strend+NAMESPACE_SEPARATOR_LENGTH==strlen);
// patend != -1, or starpati > -1 so there remains something
// to match.
// go to next pair: elt-(i+1) pat-(i+1);
stri = strend + NAMESPACE_SEPARATOR_LENGTH;
pati = (patend==-1)?pati:(patend + NAMESPACE_SEPARATOR_LENGTH);
}
}
/**
* Returns true if the ObjectName's {@code domain} is selected by the
* given {@code pattern}.
*/
public static boolean isDomainSelected(String domain, String pattern) {
if (domain == null || pattern == null)
throw new IllegalArgumentException("null");
return Util.wildpathmatch(domain,pattern);
}
/**
* Filters a set of ObjectName according to a given pattern.
*
@ -167,6 +444,34 @@ public class Util {
return res;
}
/**
* Filters a set of ObjectInstance according to a given pattern.
*
* @param pattern the pattern that the returned names must match.
* @param all the set of instances to filter.
* @return a set of ObjectInstance from which non matching instances
* have been removed.
*/
public static Set<ObjectInstance>
filterMatchingInstances(ObjectName pattern,
Set<ObjectInstance> all) {
// If no pattern, just return all names
if (pattern == null
|| all.isEmpty()
|| ObjectName.WILDCARD.equals(pattern))
return all;
// If there's a pattern, do the matching.
final Set<ObjectInstance> res = equivalentEmptySet(all);
for (ObjectInstance n : all) {
if (n == null) continue;
if (pattern.apply(n.getObjectName()))
res.add(n);
}
return res;
}
/**
* An abstract ClassLoaderRepository that contains a single class loader.
**/
@ -216,6 +521,160 @@ public class Util {
return new SingleClassLoaderRepository(loader);
}
/**
* Returns the name of the given MBeanServer that should be put in a
* permission you need.
* This corresponds to the
* {@code *[;mbeanServerName=<mbeanServerName>[;*]]} property
* embedded in the MBeanServerId attribute of the
* server's {@link MBeanServerDelegate}.
*
* @param server The MBean server
* @return the name of the MBeanServer, or "*" if the name couldn't be
* obtained, or {@value MBeanServerFactory#DEFAULT_MBEANSERVER_NAME}
* if there was no name.
*/
public static String getMBeanServerSecurityName(MBeanServer server) {
final String notfound = "*";
try {
final String mbeanServerId = (String)
server.getAttribute(MBeanServerDelegate.DELEGATE_NAME,
"MBeanServerId");
final String found = extractMBeanServerName(mbeanServerId);
if (found.length()==0)
return MBeanServerFactory.DEFAULT_MBEANSERVER_NAME;
return found;
} catch (Exception x) {
logshort("Failed to retrieve MBeanServerName for server, " +
"using \"*\"",x);
return notfound;
}
}
/**
* Returns the name of the MBeanServer embedded in the given
* mbeanServerId. If the given mbeanServerId doesn't contain any name,
* an empty String is returned.
* The MBeanServerId is expected to be of the form:
* {@code *[;mbeanServerName=<mbeanServerName>[;*]]}
* @param mbeanServerId The MBean server ID
* @return the name of the MBeanServer if found, or "" if the name was
* not present in the mbeanServerId.
*/
public static String extractMBeanServerName(String mbeanServerId) {
if (mbeanServerId==null) return "";
final String beginMarker=";mbeanServerName=";
final String endMarker=";";
final int found = mbeanServerId.indexOf(beginMarker);
if (found < 0) return "";
final int start = found + beginMarker.length();
final int stop = mbeanServerId.indexOf(endMarker, start);
return mbeanServerId.substring(start,
(stop < 0 ? mbeanServerId.length() : stop));
}
/**
* Insert the given mbeanServerName into the given mbeanServerId.
* If mbeanServerName is null, empty, or equals to "-", the returned
* mbeanServerId will not contain any mbeanServerName.
* @param mbeanServerId The mbeanServerId in which to insert
* mbeanServerName
* @param mbeanServerName The mbeanServerName
* @return an mbeanServerId containing the given mbeanServerName
* @throws IllegalArgumentException if mbeanServerId already contains
* a different name, or if the given mbeanServerName is not valid.
*/
public static String insertMBeanServerName(String mbeanServerId,
String mbeanServerName) {
final String found = extractMBeanServerName(mbeanServerId);
if (found.length() > 0 &&
found.equals(checkServerName(mbeanServerName)))
return mbeanServerId;
if (found.length() > 0 && !isMBeanServerNameUndefined(found))
throw new IllegalArgumentException(
"MBeanServerName already defined");
if (isMBeanServerNameUndefined(mbeanServerName))
return mbeanServerId;
final String beginMarker=";mbeanServerName=";
return mbeanServerId+beginMarker+checkServerName(mbeanServerName);
}
/**
* Returns true if the given mbeanServerName corresponds to an
* undefined MBeanServerName.
* The mbeanServerName is considered undefined if it is one of:
* {@code null} or {@value MBeanServerFactory#DEFAULT_MBEANSERVER_NAME}.
* @param mbeanServerName The mbeanServerName, as returned by
* {@link #extractMBeanServerName(String)}.
* @return true if the given name corresponds to one of the forms that
* denotes an undefined MBeanServerName.
*/
public static boolean isMBeanServerNameUndefined(String mbeanServerName) {
return mbeanServerName == null ||
MBeanServerFactory.DEFAULT_MBEANSERVER_NAME.equals(mbeanServerName);
}
/**
* Check that the provided mbeanServername is syntactically valid.
* @param mbeanServerName An mbeanServerName, or {@code null}.
* @return mbeanServerName, or {@value
* MBeanServerFactory#DEFAULT_MBEANSERVER_NAME} if {@code mbeanServerName}
* is {@code null}.
* @throws IllegalArgumentException if mbeanServerName contains illegal
* characters, or is empty, or is {@code "-"}.
* Illegal characters are {@value #ILLEGAL_MBEANSERVER_NAME_CHARS}.
*/
public static String checkServerName(String mbeanServerName) {
if ("".equals(mbeanServerName))
throw new IllegalArgumentException(
"\"\" is not a valid MBean server name");
if ("-".equals(mbeanServerName))
throw new IllegalArgumentException(
"\"-\" is not a valid MBean server name");
if (isMBeanServerNameUndefined(mbeanServerName))
return MBeanServerFactory.DEFAULT_MBEANSERVER_NAME;
for (char c : ILLEGAL_MBEANSERVER_NAME_CHARS.toCharArray()) {
if (mbeanServerName.indexOf(c) >= 0)
throw new IllegalArgumentException(
"invalid character in MBeanServer name: "+c);
}
return mbeanServerName;
}
/**
* Get the MBeanServer name that should be put in a permission you need.
*
* @param delegate The MBeanServerDelegate
* @return The MBeanServer name - or {@value
* MBeanServerFactory#DEFAULT_MBEANSERVER_NAME} if there was no name.
*/
public static String getMBeanServerSecurityName(
MBeanServerDelegate delegate) {
try {
final String serverName = delegate.getMBeanServerName();
if (isMBeanServerNameUndefined(serverName))
return MBeanServerFactory.DEFAULT_MBEANSERVER_NAME;
return serverName;
} catch (Exception x) {
logshort("Failed to retrieve MBeanServerName from delegate, " +
"using \"*\"",x);
return "*";
}
}
// Log the exception and its causes without logging the stack trace.
// Use with care - it is usally preferable to log the whole stack trace!
// We don't want to log the whole stack trace here: logshort() is
// called in those cases where the exception might not be abnormal.
private static void logshort(String msg, Throwable t) {
if (JmxProperties.MISC_LOGGER.isLoggable(Level.FINE)) {
StringBuilder toprint = new StringBuilder(msg);
toprint.append("\nCaused By: ").append(String.valueOf(t));
while ((t=t.getCause())!=null)
toprint.append("\nCaused By: ").append(String.valueOf(t));
JmxProperties.MISC_LOGGER.fine(toprint.toString());
}
}
public static <T> Set<T> cloneSet(Set<T> set) {
if (set instanceof SortedSet) {
@SuppressWarnings("unchecked")
@ -232,10 +691,19 @@ public class Util {
@SuppressWarnings("unchecked")
SortedSet<T> sset = (SortedSet<T>) set;
set = new TreeSet<T>(sset.comparator());
} else if (set != null) {
set = new HashSet<T>(set.size());
} else
set = new HashSet<T>();
return set;
}
// This exception is used when wrapping a class that throws IOException
// in a class that doesn't.
// The typical example for this are JMXNamespaces, when the sub
// MBeanServer can be remote.
//
public static RuntimeException newRuntimeIOException(IOException io) {
final String msg = "Communication failed with underlying resource: "+
io.getMessage();
return new RuntimeException(msg,io);
}
}

View file

@ -0,0 +1,475 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace;
import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.mbeanserver.Util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.InstanceNotFoundException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanPermission;
import javax.management.MBeanServerDelegate;
import javax.management.MBeanServerNotification;
import javax.management.MalformedObjectNameException;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.namespace.JMXDomain;
/**
* A DomainInterceptor wraps a JMXDomain.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
public class DomainInterceptor extends HandlerInterceptor<JMXDomain> {
// TODO: Ideally DomainInterceptor should be replaced by
// something at Repository level.
// The problem there will be that we may need to
// reinstantiate the 'queryPerformedByRepos' boolean
// [or we will need to wrap the repository in
// a 'RepositoryInterceptor'?]
// Also there's no real need for a DomainInterceptor to
// extend RewritingMBeanServerConnection.
/**
* A logger for this class.
**/
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
private final String domainName;
private volatile ObjectName ALL;
private final String serverName;
private volatile NotificationListener mbsListener;
private static class PatternNotificationFilter
implements NotificationFilter {
final ObjectName pattern;
public PatternNotificationFilter(ObjectName pattern) {
this.pattern = pattern;
}
public boolean isNotificationEnabled(Notification notification) {
if (!(notification instanceof MBeanServerNotification))
return false;
final MBeanServerNotification mbsn =
(MBeanServerNotification) notification;
if (pattern == null || pattern.apply(mbsn.getMBeanName()))
return true;
return false;
}
static final long serialVersionUID = 7409950927025262111L;
}
/**
* Creates a new instance of NamespaceInterceptor
*/
public DomainInterceptor(String serverName,
JMXDomain handler,
String domainName) {
super(handler);
this.domainName = domainName;
this.serverName = serverName;
}
@Override
public String toString() {
return this.getClass().getName()+"(parent="+serverName+
", domain="+this.domainName+")";
}
public void connectDelegate(final MBeanServerDelegate delegate)
throws InstanceNotFoundException {
final NotificationFilter filter =
new PatternNotificationFilter(getPatternFor(null));
synchronized (this) {
if (mbsListener == null)
mbsListener = new NotificationListener() {
public void handleNotification(Notification notification,
Object handback) {
if (filter.isNotificationEnabled(notification))
delegate.sendNotification(notification);
}
};
}
getNamespace().
addMBeanServerNotificationListener(mbsListener, filter);
}
public void disconnectDelegate()
throws InstanceNotFoundException, ListenerNotFoundException {
final NotificationListener l;
synchronized (this) {
l = mbsListener;
if (l == null) return;
mbsListener = null;
}
getNamespace().removeMBeanServerNotificationListener(l);
}
public void addPostRegisterTask(Queue<Runnable> queue,
final MBeanServerDelegate delegate) {
if (queue == null)
throw new IllegalArgumentException("task queue must not be null");
final Runnable task1 = new Runnable() {
public void run() {
try {
connectDelegate(delegate);
} catch (Exception x) {
throw new UnsupportedOperationException("notification forwarding",x);
}
}
};
queue.add(task1);
}
public void addPostDeregisterTask(Queue<Runnable> queue,
final MBeanServerDelegate delegate) {
if (queue == null)
throw new IllegalArgumentException("task queue must not be null");
final Runnable task1 = new Runnable() {
public void run() {
try {
disconnectDelegate();
} catch (Exception x) {
throw new UnsupportedOperationException("notification forwarding",x);
}
}
};
queue.add(task1);
}
/**
* Throws IllegalArgumentException if targetName.getDomain() is not
* in the domain handled.
**/
@Override
protected ObjectName toSource(ObjectName targetName) {
if (targetName == null) return null;
if (targetName.isDomainPattern()) return targetName;
final String targetDomain = targetName.getDomain();
// TODO: revisit this. RuntimeOperationsException may be better?
//
if (!targetDomain.equals(domainName))
throw new IllegalArgumentException(targetName.toString());
return targetName;
}
@Override
protected ObjectName toTarget(ObjectName sourceName) {
return sourceName;
}
/**
* No rewriting: always return sources - stripping instances for which
* the caller doesn't have permissions.
**/
@Override
Set<ObjectInstance> processOutputInstances(Set<ObjectInstance> sources) {
if (sources == null || sources.isEmpty() || !checkOn())
return sources;
final Set<ObjectInstance> res = Util.equivalentEmptySet(sources);
for (ObjectInstance o : sources) {
if (checkQuery(o.getObjectName(), "queryMBeans"))
res.add(o);
}
return res;
}
/**
* No rewriting: always return sourceNames - stripping names for which
* the caller doesn't have permissions.
**/
@Override
Set<ObjectName> processOutputNames(Set<ObjectName> sourceNames) {
if (sourceNames == null || sourceNames.isEmpty() || !checkOn())
return sourceNames;
final Set<ObjectName> res = Util.equivalentEmptySet(sourceNames);
for (ObjectName o : sourceNames) {
if (checkQuery(o, "queryNames"))
res.add(o);
}
return res;
}
/** No rewriting: always return source **/
@Override
ObjectInstance processOutputInstance(ObjectInstance source) {
return source;
}
@Override
public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
try {
// We don't trust the wrapped JMXDomain...
final ObjectName pattern = getPatternFor(name);
final Set<ObjectName> res = super.queryNames(pattern,query);
return Util.filterMatchingNames(pattern,res);
} catch (Exception x) {
if (LOG.isLoggable(Level.FINE))
LOG.fine("Unexpected exception raised in queryNames: "+x);
LOG.log(Level.FINEST,"Unexpected exception raised in queryNames",x);
}
// We reach here only when an exception was raised.
//
final Set<ObjectName> empty = Collections.emptySet();
return empty;
}
private ObjectName getPatternFor(final ObjectName name) {
try {
if (ALL == null) ALL = ObjectName.getInstance(domainName + ":*");
if (name == null) return ALL;
if (name.getDomain().equals(domainName)) return name;
return name.withDomain(domainName);
} catch (MalformedObjectNameException x) {
throw new IllegalArgumentException(String.valueOf(name),x);
}
}
@Override
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
try {
// We don't trust the wrapped JMXDomain...
final ObjectName pattern = getPatternFor(name);
final Set<ObjectInstance> res = super.queryMBeans(pattern,query);
return Util.filterMatchingInstances(pattern,res);
} catch (Exception x) {
if (LOG.isLoggable(Level.FINE))
LOG.fine("Unexpected exception raised in queryNames: "+x);
LOG.log(Level.FINEST,"Unexpected exception raised in queryNames",x);
}
// We reach here only when an exception was raised.
//
final Set<ObjectInstance> empty = Collections.emptySet();
return empty;
}
@Override
public String getDefaultDomain() {
return domainName;
}
@Override
public String[] getDomains() {
return new String[] {domainName};
}
// We call getMBeanCount() on the namespace rather than on the
// source server in order to avoid counting MBeans which are not
// in the domain.
@Override
public Integer getMBeanCount() {
return getNamespace().getMBeanCount();
}
private boolean checkOn() {
final SecurityManager sm = System.getSecurityManager();
return (sm != null);
}
//
// Implements permission checks.
//
@Override
void check(ObjectName routingName, String member, String action) {
if (!checkOn()) return;
final String act = (action==null)?"-":action.intern();
if(act == "queryMBeans" || act == "queryNames") { // ES: OK
// This is tricky. check with 3 parameters is called
// by queryNames/queryMBeans before performing the query.
// At this point we must check with no class name.
// Therefore we pass a className of "-".
// The filtering will be done later - processOutputNames and
// processOutputInstance will call checkQuery.
//
check(routingName, "-", "-", act);
} else {
// This is also tricky:
// passing null here will cause check to retrieve the classname,
// if needed.
check(routingName, null, member, act);
}
}
//
// Implements permission checks.
//
@Override
void checkCreate(ObjectName routingName, String className, String action) {
if (!checkOn()) return;
check(routingName,className,"-",action);
}
//
// Implements permission checks.
//
void check(ObjectName routingName, String className, String member,
String action) {
if (!checkOn()) return;
final MBeanPermission perm;
// action is most probably already an intern string.
// string literals are intern strings.
// we create a new intern string for 'action' - just to be on
// the safe side...
// We intern it in order to be able to use == rather than equals
// below, because if we don't, and if action is not one of the
// 4 literals below, we would have to do a full string comparison.
//
final String act = (action==null)?"-":action.intern();
if (act == "getDomains") { // ES: OK
perm = new MBeanPermission(serverName,"-",member,
routingName,act);
} else {
final String clazz =
(className==null)?getClassName(routingName):className;
perm = new MBeanPermission(serverName,clazz,member,
routingName,act);
}
final SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(perm);
}
String getClassName(ObjectName routingName) {
if (routingName == null || routingName.isPattern()) return "-";
try {
return getNamespace().getSourceServer().
getObjectInstance(routingName).getClassName();
} catch (InstanceNotFoundException ex) {
LOG.finest("Can't get class name for "+routingName+
", using \"-\". Cause is: "+ex);
return "-";
}
}
//
// Implements permission filters for attributes...
//
@Override
AttributeList checkAttributes(ObjectName routingName,
AttributeList attributes, String action) {
if (!checkOn()) return attributes;
final String className = getClassName(routingName);
check(routingName,className,"-",action);
if (attributes == null || attributes.isEmpty()) return attributes;
final AttributeList res = new AttributeList();
for (Attribute at : attributes.asList()) {
try {
check(routingName,className,at.getName(),action);
res.add(at);
} catch (SecurityException x) { // DLS: OK
continue;
}
}
return res;
}
//
// Implements permission filters for attributes...
//
@Override
String[] checkAttributes(ObjectName routingName, String[] attributes,
String action) {
if (!checkOn()) return attributes;
final String className = getClassName(routingName);
check(routingName,className,"-",action);
if (attributes == null || attributes.length==0) return attributes;
final List<String> res = new ArrayList<String>(attributes.length);
for (String at : attributes) {
try {
check(routingName,className,at,action);
res.add(at);
} catch (SecurityException x) { // DLS: OK
continue;
}
}
return res.toArray(new String[res.size()]);
}
//
// Implements permission filters for domains...
//
@Override
String[] checkDomains(String[] domains, String action) {
if (domains == null || domains.length==0 || !checkOn())
return domains;
int count=0;
for (int i=0;i<domains.length;i++) {
try {
check(Util.newObjectName(domains[i]+":x=x"),"-",
"-","getDomains");
} catch (SecurityException x) { // DLS: OK
count++;
domains[i]=null;
}
}
if (count == 0) return domains;
final String[] res = new String[domains.length-count];
count = 0;
for (int i=0;i<domains.length;i++)
if (domains[i]!=null) res[count++]=domains[i];
return res;
}
//
// Implements permission filters for queries...
//
@Override
boolean checkQuery(ObjectName routingName, String action) {
try {
final String className = getClassName(routingName);
check(routingName,className,"-",action);
return true;
} catch (SecurityException x) { // DLS: OK
return false;
}
}
}

View file

@ -0,0 +1,577 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace;
import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.interceptor.MBeanServerInterceptor;
import com.sun.jmx.mbeanserver.Util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.OperationsException;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.management.RuntimeOperationsException;
import javax.management.loading.ClassLoaderRepository;
import javax.management.namespace.JMXNamespace;
/**
* This interceptor wraps a JMXNamespace, and performs
* {@code ObjectName} rewriting. {@code HandlerInterceptor} are
* usually created and managed by a {@link NamespaceDispatcher} or
* {@link DomainDispatcher}.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
public abstract class HandlerInterceptor<T extends JMXNamespace>
extends RoutingMBeanServerConnection<MBeanServer>
implements MBeanServerInterceptor {
/**
* A logger for this class.
**/
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
// The wrapped JMXNamespace
private final T handler;
/**
* Creates a new instance of HandlerInterceptor
*/
public HandlerInterceptor(T handler) {
if (handler == null) throw new IllegalArgumentException("null");
this.handler = handler;
}
@Override
protected MBeanServer source() {
return handler.getSourceServer();
}
// The MBeanServer on which getClassLoader / getClassLoaderFor
// will be called.
// The NamespaceInterceptor overrides this method - so that it
// getClassLoader / getClassLoaderFor don't trigger the loop
// detection mechanism.
//
MBeanServer getServerForLoading() {
return source();
}
T getNamespace() {
return handler;
}
// If the underlying JMXNamespace throws an IO, the IO will be
// wrapped in a RuntimeOperationsException.
RuntimeException handleIOException(IOException x,String fromMethodName,
Object... params) {
// Must do something here?
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("IO Exception in "+fromMethodName+": "+x+
" - "+" rethrowing as RuntimeOperationsException.");
}
throw new RuntimeOperationsException(
Util.newRuntimeIOException(x));
}
// From MBeanServer: catch & handles IOException
@Override
public AttributeList getAttributes(ObjectName name, String[] attributes)
throws InstanceNotFoundException, ReflectionException {
try {
return super.getAttributes(name, attributes);
} catch (IOException ex) {
throw handleIOException(ex,"getAttributes",name,attributes);
}
}
// From MBeanServer
public ClassLoader getClassLoaderFor(ObjectName mbeanName)
throws InstanceNotFoundException {
final ObjectName sourceName = toSourceOrRuntime(mbeanName);
try {
check(mbeanName,null,"getClassLoaderFor");
return getServerForLoading().getClassLoaderFor(sourceName);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// From MBeanServer
public ClassLoader getClassLoader(ObjectName loaderName)
throws InstanceNotFoundException {
final ObjectName sourceName = toSourceOrRuntime(loaderName);
try {
check(loaderName,null,"getClassLoader");
return getServerForLoading().getClassLoader(sourceName);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// From MBeanServer
public ObjectInstance registerMBean(Object object, ObjectName name)
throws InstanceAlreadyExistsException, MBeanRegistrationException,
NotCompliantMBeanException {
final ObjectName sourceName = newSourceMBeanName(name);
try {
checkCreate(name,object.getClass().getName(),"registerMBean");
return processOutputInstance(
source().registerMBean(object,sourceName));
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// From MBeanServer: catch & handles IOException
@Override
public void removeNotificationListener(ObjectName name, ObjectName listener)
throws InstanceNotFoundException, ListenerNotFoundException {
try {
super.removeNotificationListener(name, listener);
} catch (IOException ex) {
throw handleIOException(ex,"removeNotificationListener",name,listener);
}
}
// From MBeanServer: catch & handles IOException
@Override
public String getDefaultDomain() {
try {
return super.getDefaultDomain();
} catch (IOException ex) {
throw handleIOException(ex,"getDefaultDomain");
}
}
// From MBeanServer: catch & handles IOException
@Override
public String[] getDomains() {
try {
return super.getDomains();
} catch (IOException ex) {
throw handleIOException(ex,"getDomains");
}
}
// From MBeanServer: catch & handles IOException
@Override
public Integer getMBeanCount() {
try {
return super.getMBeanCount();
} catch (IOException ex) {
throw handleIOException(ex,"getMBeanCount");
}
}
// From MBeanServer: catch & handles IOException
@Override
public void setAttribute(ObjectName name, Attribute attribute)
throws InstanceNotFoundException, AttributeNotFoundException,
InvalidAttributeValueException, MBeanException,
ReflectionException {
try {
super.setAttribute(name, attribute);
} catch (IOException ex) {
throw handleIOException(ex,"setAttribute",name, attribute);
}
}
// From MBeanServer: catch & handles IOException
@Override
public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
try {
return super.queryNames(name, query);
} catch (IOException ex) {
throw handleIOException(ex,"queryNames",name, query);
}
}
// From MBeanServer: catch & handles IOException
@Override
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
try {
return super.queryMBeans(name, query);
} catch (IOException ex) {
throw handleIOException(ex,"queryMBeans",name, query);
}
}
// From MBeanServer: catch & handles IOException
@Override
public boolean isInstanceOf(ObjectName name, String className)
throws InstanceNotFoundException {
try {
return super.isInstanceOf(name, className);
} catch (IOException ex) {
throw handleIOException(ex,"isInstanceOf",name, className);
}
}
// From MBeanServer: catch & handles IOException
@Override
public ObjectInstance createMBean(String className, ObjectName name)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException {
try {
return super.createMBean(className, name);
} catch (IOException ex) {
throw handleIOException(ex,"createMBean",className, name);
}
}
// From MBeanServer: catch & handles IOException
@Override
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException, InstanceNotFoundException {
try {
return super.createMBean(className, name, loaderName);
} catch (IOException ex) {
throw handleIOException(ex,"createMBean",className, name, loaderName);
}
}
// From MBeanServer: catch & handles IOException
@Override
public Object getAttribute(ObjectName name, String attribute)
throws MBeanException, AttributeNotFoundException,
InstanceNotFoundException, ReflectionException {
try {
return super.getAttribute(name, attribute);
} catch (IOException ex) {
throw handleIOException(ex,"getAttribute",name, attribute);
}
}
// From MBeanServer: catch & handles IOException
@Override
public void removeNotificationListener(ObjectName name, ObjectName listener,
NotificationFilter filter, Object handback)
throws InstanceNotFoundException, ListenerNotFoundException {
try {
super.removeNotificationListener(name, listener, filter, handback);
} catch (IOException ex) {
throw handleIOException(ex,"removeNotificationListener",name,
listener, filter, handback);
}
}
// From MBeanServer: catch & handles IOException
@Override
public void removeNotificationListener(ObjectName name,
NotificationListener listener, NotificationFilter filter,
Object handback)
throws InstanceNotFoundException, ListenerNotFoundException {
try {
super.removeNotificationListener(name, listener, filter, handback);
} catch (IOException ex) {
throw handleIOException(ex,"removeNotificationListener",name,
listener, filter, handback);
}
}
// From MBeanServer: catch & handles IOException
@Override
public void removeNotificationListener(ObjectName name,
NotificationListener listener)
throws InstanceNotFoundException, ListenerNotFoundException {
try {
super.removeNotificationListener(name, listener);
} catch (IOException ex) {
throw handleIOException(ex,"removeNotificationListener",name,
listener);
}
}
// From MBeanServer: catch & handles IOException
@Override
public void addNotificationListener(ObjectName name,
NotificationListener listener, NotificationFilter filter,
Object handback) throws InstanceNotFoundException {
try {
super.addNotificationListener(name, listener, filter, handback);
} catch (IOException ex) {
throw handleIOException(ex,"addNotificationListener",name,
listener, filter, handback);
}
}
// From MBeanServer: catch & handles IOException
@Override
public void addNotificationListener(ObjectName name, ObjectName listener,
NotificationFilter filter, Object handback)
throws InstanceNotFoundException {
try {
super.addNotificationListener(name, listener, filter, handback);
} catch (IOException ex) {
throw handleIOException(ex,"addNotificationListener",name,
listener, filter, handback);
}
}
// From MBeanServer: catch & handles IOException
@Override
public boolean isRegistered(ObjectName name) {
try {
return super.isRegistered(name);
} catch (IOException ex) {
throw handleIOException(ex,"isRegistered",name);
}
}
// From MBeanServer: catch & handles IOException
@Override
public void unregisterMBean(ObjectName name)
throws InstanceNotFoundException, MBeanRegistrationException {
try {
super.unregisterMBean(name);
} catch (IOException ex) {
throw handleIOException(ex,"unregisterMBean",name);
}
}
// From MBeanServer: catch & handles IOException
@Override
public MBeanInfo getMBeanInfo(ObjectName name)
throws InstanceNotFoundException, IntrospectionException,
ReflectionException {
try {
return super.getMBeanInfo(name);
} catch (IOException ex) {
throw handleIOException(ex,"getMBeanInfo",name);
}
}
// From MBeanServer: catch & handles IOException
@Override
public ObjectInstance getObjectInstance(ObjectName name)
throws InstanceNotFoundException {
try {
return super.getObjectInstance(name);
} catch (IOException ex) {
throw handleIOException(ex,"getObjectInstance",name);
}
}
// From MBeanServer: catch & handles IOException
@Override
public ObjectInstance createMBean(String className, ObjectName name,
Object[] params, String[] signature)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException {
try {
return super.createMBean(className, name, params, signature);
} catch (IOException ex) {
throw handleIOException(ex,"createMBean",className, name,
params, signature);
}
}
// From MBeanServer: catch & handles IOException
@Override
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName, Object[] params, String[] signature)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException, InstanceNotFoundException {
try {
return super.createMBean(className, name, loaderName, params,
signature);
} catch (IOException ex) {
throw handleIOException(ex,"createMBean",className, name,loaderName,
params, signature);
}
}
// From MBeanServer: catch & handles IOException
@Override
public AttributeList setAttributes(ObjectName name,AttributeList attributes)
throws InstanceNotFoundException, ReflectionException {
try {
return super.setAttributes(name, attributes);
} catch (IOException ex) {
throw handleIOException(ex,"setAttributes",name, attributes);
}
}
// From MBeanServer: catch & handles IOException
@Override
public Object invoke(ObjectName name, String operationName, Object[] params,
String[] signature)
throws InstanceNotFoundException, MBeanException, ReflectionException {
try {
return super.invoke(name, operationName, params, signature);
} catch (IOException ex) {
throw handleIOException(ex,"invoke",name, operationName,
params, signature);
}
}
//
// These methods are inherited from MBeanServer....
//
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
public Object instantiate(String className)
throws ReflectionException, MBeanException {
if (LOG.isLoggable(Level.FINE))
LOG.fine("call to unsupported instantiate method: " +
"trowing UnsupportedOperationException");
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
public Object instantiate(String className, ObjectName loaderName)
throws ReflectionException, MBeanException,
InstanceNotFoundException {
if (LOG.isLoggable(Level.FINE))
LOG.fine("call to unsupported method: instantiate(...) -" +
"throwing UnsupportedOperationException");
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
public Object instantiate(String className, Object[] params,
String[] signature) throws ReflectionException, MBeanException {
if (LOG.isLoggable(Level.FINE))
LOG.fine("call to unsupported method: instantiate(...) -" +
"throwing UnsupportedOperationException");
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
public Object instantiate(String className, ObjectName loaderName,
Object[] params, String[] signature)
throws ReflectionException, MBeanException,
InstanceNotFoundException {
if (LOG.isLoggable(Level.FINE))
LOG.fine("call to unsupported method: instantiate(...) -" +
"throwing UnsupportedOperationException");
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
@Deprecated
public ObjectInputStream deserialize(ObjectName name, byte[] data)
throws InstanceNotFoundException, OperationsException {
if (LOG.isLoggable(Level.FINE))
LOG.fine("call to unsupported method: deserialize(...) -" +
"throwing UnsupportedOperationException");
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
@Deprecated
public ObjectInputStream deserialize(String className, byte[] data)
throws OperationsException, ReflectionException {
if (LOG.isLoggable(Level.FINE))
LOG.fine("call to unsupported method: deserialize(...) -" +
"throwing UnsupportedOperationException");
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
@Deprecated
public ObjectInputStream deserialize(String className,
ObjectName loaderName, byte[] data)
throws InstanceNotFoundException, OperationsException,
ReflectionException {
if (LOG.isLoggable(Level.FINE))
LOG.fine("call to unsupported method: deserialize(...) -" +
"throwing UnsupportedOperationException");
throw new UnsupportedOperationException("Not applicable.");
}
/**
* This method should never be called.
* Throws UnsupportedOperationException.
*/
public ClassLoaderRepository getClassLoaderRepository() {
if (LOG.isLoggable(Level.FINE))
LOG.fine("call to unsupported method: getClassLoaderRepository() -" +
"throwing UnsupportedOperationException");
throw new UnsupportedOperationException("Not applicable.");
}
static RuntimeException newUnsupportedException(String namespace) {
return new RuntimeOperationsException(
new UnsupportedOperationException(
"Not supported in this namespace: "+namespace));
}
}

View file

@ -0,0 +1,369 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace;
import com.sun.jmx.defaults.JmxProperties;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanServerConnection;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.event.EventClient;
import javax.management.namespace.JMXNamespaces;
import javax.management.remote.JMXAddressable;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXServiceURL;
import javax.security.auth.Subject;
/**
* A collection of methods that provide JMXConnector wrappers for
* JMXRemoteNamepaces underlying connectors.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
public final class JMXNamespaceUtils {
/**
* A logger for this class.
**/
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
private static <K,V> Map<K,V> newWeakHashMap() {
return new WeakHashMap<K,V>();
}
/** Creates a new instance of JMXNamespaces */
private JMXNamespaceUtils() {
}
/**
* Returns an unmodifiable option map in which the given keys have been
* filtered out.
* @param keys keys to filter out from the map.
* @return An unmodifiable option map in which the given keys have been
* filtered out.
*/
public static <K,V> Map<K,V> filterMap(Map<K,V> map, K... keys) {
final Map<K,V> filtered;
filtered=new HashMap<K,V>(map);
for (K key : keys) {
filtered.remove(key);
}
return unmodifiableMap(filtered);
}
// returns un unmodifiable view of a map.
public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> aMap) {
if (aMap == null || aMap.isEmpty())
return Collections.emptyMap();
return Collections.unmodifiableMap(aMap);
}
/**
* A base class that helps writing JMXConnectors that return
* MBeanServerConnection wrappers.
* This base class wraps an inner JMXConnector (the source), and preserve
* its caching policy. If a connection is cached in the source, its wrapper
* will be cached in this connector too.
* Author's note: rewriting this with java.lang.reflect.Proxy could be
* envisaged. It would avoid the combinatory sub-classing introduced by
* JMXAddressable.
* <p>
* Note: all the standard JMXConnector implementations are serializable.
* This implementation here is not. Should it be?
* I believe it must not be serializable unless it becomes
* part of a public API (either standard or officially exposed
* and supported in a documented com.sun package)
**/
static class JMXCachingConnector
implements JMXConnector {
// private static final long serialVersionUID = -2279076110599707875L;
final JMXConnector source;
// if this object is made serializable, then the variable below
// needs to become volatile transient and be lazyly-created...
private final
Map<MBeanServerConnection,MBeanServerConnection> connectionMap;
public JMXCachingConnector(JMXConnector source) {
this.source = checkNonNull(source, "source");
connectionMap = newWeakHashMap();
}
private MBeanServerConnection
getCached(MBeanServerConnection inner) {
return connectionMap.get(inner);
}
private MBeanServerConnection putCached(final MBeanServerConnection inner,
final MBeanServerConnection wrapper) {
if (inner == wrapper) return wrapper;
synchronized (this) {
final MBeanServerConnection concurrent =
connectionMap.get(inner);
if (concurrent != null) return concurrent;
connectionMap.put(inner,wrapper);
}
return wrapper;
}
public void addConnectionNotificationListener(NotificationListener
listener, NotificationFilter filter, Object handback) {
source.addConnectionNotificationListener(listener,filter,handback);
}
public void close() throws IOException {
source.close();
}
public void connect() throws IOException {
source.connect();
}
public void connect(Map<String,?> env) throws IOException {
source.connect(env);
}
public String getConnectionId() throws IOException {
return source.getConnectionId();
}
/**
* Preserve caching policy of the underlying connector.
**/
public MBeanServerConnection
getMBeanServerConnection() throws IOException {
final MBeanServerConnection inner =
source.getMBeanServerConnection();
final MBeanServerConnection cached = getCached(inner);
if (cached != null) return cached;
final MBeanServerConnection wrapper = wrap(inner);
return putCached(inner,wrapper);
}
public MBeanServerConnection
getMBeanServerConnection(Subject delegationSubject)
throws IOException {
final MBeanServerConnection wrapped =
source.getMBeanServerConnection(delegationSubject);
synchronized (this) {
final MBeanServerConnection cached = getCached(wrapped);
if (cached != null) return cached;
final MBeanServerConnection wrapper =
wrapWithSubject(wrapped,delegationSubject);
return putCached(wrapped,wrapper);
}
}
public void removeConnectionNotificationListener(
NotificationListener listener)
throws ListenerNotFoundException {
source.removeConnectionNotificationListener(listener);
}
public void removeConnectionNotificationListener(
NotificationListener l, NotificationFilter f,
Object handback) throws ListenerNotFoundException {
source.removeConnectionNotificationListener(l,f,handback);
}
/**
* This is the method that subclass will redefine. This method
* is called by {@code this.getMBeanServerConnection()}.
* {@code inner} is the connection returned by
* {@code source.getMBeanServerConnection()}.
**/
protected MBeanServerConnection wrap(MBeanServerConnection inner)
throws IOException {
return inner;
}
/**
* Subclass may also want to redefine this method.
* By default it calls wrap(inner). This method
* is called by {@code this.getMBeanServerConnection(Subject)}.
* {@code inner} is the connection returned by
* {@code source.getMBeanServerConnection(Subject)}.
**/
protected MBeanServerConnection wrapWithSubject(
MBeanServerConnection inner, Subject delegationSubject)
throws IOException {
return wrap(inner);
}
@Override
public String toString() {
if (source instanceof JMXAddressable) {
final JMXServiceURL address =
((JMXAddressable)source).getAddress();
if (address != null)
return address.toString();
}
return source.toString();
}
}
/**
* The name space connector can do 'cd'
**/
static class JMXNamespaceConnector extends JMXCachingConnector {
// private static final long serialVersionUID = -4813611540843020867L;
private final String toDir;
private final boolean closeable;
public JMXNamespaceConnector(JMXConnector source, String toDir,
boolean closeable) {
super(source);
this.toDir = toDir;
this.closeable = closeable;
}
@Override
public void close() throws IOException {
if (!closeable)
throw new UnsupportedOperationException("close");
else super.close();
}
@Override
protected MBeanServerConnection wrap(MBeanServerConnection wrapped)
throws IOException {
if (LOG.isLoggable(Level.FINER))
LOG.finer("Creating name space proxy connection for source: "+
"namespace="+toDir);
return JMXNamespaces.narrowToNamespace(wrapped,toDir);
}
@Override
public String toString() {
return "JMXNamespaces.narrowToNamespace("+
super.toString()+
", \""+toDir+"\")";
}
}
static class JMXEventConnector extends JMXCachingConnector {
// private static final long serialVersionUID = 4742659236340242785L;
JMXEventConnector(JMXConnector wrapped) {
super(wrapped);
}
@Override
protected MBeanServerConnection wrap(MBeanServerConnection inner)
throws IOException {
return EventClient.getEventClientConnection(inner);
}
@Override
public String toString() {
return "EventClient.withEventClient("+super.toString()+")";
}
}
static class JMXAddressableEventConnector extends JMXEventConnector
implements JMXAddressable {
// private static final long serialVersionUID = -9128520234812124712L;
JMXAddressableEventConnector(JMXConnector wrapped) {
super(wrapped);
}
public JMXServiceURL getAddress() {
return ((JMXAddressable)source).getAddress();
}
}
/**
* Creates a connector whose MBeamServerConnection will point to the
* given sub name space inside the source connector.
* @see JMXNamespace
**/
public static JMXConnector cd(final JMXConnector source,
final String toNamespace,
final boolean closeable)
throws IOException {
checkNonNull(source, "JMXConnector");
if (toNamespace == null || toNamespace.equals(""))
return source;
return new JMXNamespaceConnector(source,toNamespace,closeable);
}
/**
* Returns a JMX Connector that will use an {@link EventClient}
* to subscribe for notifications. If the server doesn't have
* an {@link EventClientDelegateMBean}, then the connector will
* use the legacy notification mechanism instead.
*
* @param source The underlying JMX Connector wrapped by the returned
* connector.
* @return A JMX Connector that will uses an {@link EventClient}, if
* available.
* @see EventClient#getEventClientConnection(MBeanServerConnection)
*/
public static JMXConnector withEventClient(final JMXConnector source) {
checkNonNull(source, "JMXConnector");
if (source instanceof JMXAddressable)
return new JMXAddressableEventConnector(source);
else
return new JMXEventConnector(source);
}
public static <T> T checkNonNull(T parameter, String name) {
if (parameter == null)
throw new IllegalArgumentException(name+" must not be null");
return parameter;
}
}

View file

@ -0,0 +1,449 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace;
import com.sun.jmx.defaults.JmxProperties;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Logger;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.namespace.JMXNamespaces;
import javax.management.namespace.JMXNamespace;
import javax.management.namespace.JMXNamespacePermission;
/**
* A NamespaceInterceptor wraps a JMXNamespace, performing
* ObjectName rewriting.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
public class NamespaceInterceptor extends HandlerInterceptor<JMXNamespace> {
/**
* A logger for this class.
**/
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
private static final Logger PROBE_LOG = Logger.getLogger(
JmxProperties.NAMESPACE_LOGGER+".probe");
// The target name space in which the NamepsaceHandler is mounted.
private final String targetNs;
private final String serverName;
private final ObjectNameRouter proc;
/**
* Internal hack. The JMXRemoteNamespace can be closed and reconnected.
* Each time the JMXRemoteNamespace connects, a probe should be sent
* to detect cycle. The MBeanServer exposed by JMXRemoteNamespace thus
* implements the DynamicProbe interface, which makes it possible for
* this handler to know that it should send a new probe.
*
* XXX: TODO this probe thing is way too complex and fragile.
* This *must* go away or be replaced by something simpler.
* ideas are welcomed.
**/
public static interface DynamicProbe {
public boolean isProbeRequested();
}
/**
* Creates a new instance of NamespaceInterceptor
*/
public NamespaceInterceptor(
String serverName,
JMXNamespace handler,
String targetNamespace) {
super(handler);
this.serverName = serverName;
this.targetNs =
ObjectNameRouter.normalizeNamespacePath(targetNamespace,
true, true, false);
proc = new ObjectNameRouter(targetNamespace, "");
}
@Override
public String toString() {
return this.getClass().getName()+"(parent="+serverName+
", namespace="+this.targetNs+")";
}
/*
* XXX: TODO this probe thing is way too complex and fragile.
* This *must* go away or be replaced by something simpler.
* ideas are welcomed.
*/
private volatile boolean probed = false;
private volatile ObjectName probe;
// Query Pattern that we will send through the source server in order
// to detect self-linking namespaces.
//
// XXX: TODO this probe thing is way too complex and fragile.
// This *must* go away or be replaced by something simpler.
// ideas are welcomed.
final ObjectName makeProbePattern(ObjectName probe)
throws MalformedObjectNameException {
// we could probably link the probe pattern with the probe - e.g.
// using the UUID as key in the pattern - but is it worth it? it
// also has some side effects on the context namespace - because
// such a probe may get rejected by the jmx.context// namespace.
//
// The trick here is to devise a pattern that is not likely to
// be blocked by intermediate levels. Querying for all namespace
// handlers in the source (or source namespace) is more likely to
// achieve this goal.
//
return ObjectName.getInstance("*" +
JMXNamespaces.NAMESPACE_SEPARATOR + ":" +
JMXNamespace.TYPE_ASSIGNMENT);
}
// tell whether the name pattern corresponds to what might have been
// sent as a probe.
// XXX: TODO this probe thing is way too complex and fragile.
// This *must* go away or be replaced by something simpler.
// ideas are welcomed.
final boolean isProbePattern(ObjectName name) {
final ObjectName p = probe;
if (p == null) return false;
try {
return String.valueOf(name).endsWith(targetNs+
JMXNamespaces.NAMESPACE_SEPARATOR + "*" +
JMXNamespaces.NAMESPACE_SEPARATOR + ":" +
JMXNamespace.TYPE_ASSIGNMENT);
} catch (RuntimeException x) {
// should not happen.
PROBE_LOG.finest("Ignoring unexpected exception in self link detection: "+
x);
return false;
}
}
// The first time a request reaches this NamespaceInterceptor, the
// interceptor will send a probe to detect whether the underlying
// JMXNamespace links to itslef.
//
// One way to create such self-linking namespace would be for instance
// to create a JMXNamespace whose getSourceServer() method would return:
// JMXNamespaces.narrowToNamespace(getMBeanServer(),
// getObjectName().getDomain())
//
// If such an MBeanServer is returned, then any call to that MBeanServer
// will trigger an infinite loop.
// There can be even trickier configurations if remote connections are
// involved.
//
// In order to prevent this from happening, the NamespaceInterceptor will
// send a probe, in an attempt to detect whether it will receive it at
// the other end. If the probe is received, an exception will be thrown
// in order to break the recursion. The probe is only sent once - when
// the first request to the namespace occurs. The DynamicProbe interface
// can also be used by a Sun JMXNamespace implementation to request the
// emission of a probe at any time (see JMXRemoteNamespace
// implementation).
//
// Probes work this way: the NamespaceInterceptor sets a flag and sends
// a queryNames() request. If a queryNames() request comes in when the flag
// is on, then it deduces that there is a self-linking loop - and instead
// of calling queryNames() on the source MBeanServer of the JMXNamespace
// handler (which would cause the loop to go on) it breaks the recursion
// by returning the probe ObjectName.
// If the NamespaceInterceptor receives the probe ObjectName as result of
// its original sendProbe() request it knows that it has been looping
// back on itslef and throws an IOException...
//
//
// XXX: TODO this probe thing is way too complex and fragile.
// This *must* go away or be replaced by something simpler.
// ideas are welcomed.
//
final void sendProbe(MBeanServerConnection msc)
throws IOException {
try {
PROBE_LOG.fine("Sending probe");
// This is just to prevent any other thread to modify
// the probe while the detection cycle is in progress.
//
final ObjectName probePattern;
// we don't want to synchronize on this - we use targetNs
// because it's non null and final.
synchronized (targetNs) {
probed = false;
if (probe != null) {
throw new IOException("concurent connection in progress");
}
final String uuid = UUID.randomUUID().toString();
final String endprobe =
JMXNamespaces.NAMESPACE_SEPARATOR + uuid +
":type=Probe,key="+uuid;
final ObjectName newprobe =
ObjectName.getInstance(endprobe);
probePattern = makeProbePattern(newprobe);
probe = newprobe;
}
try {
PROBE_LOG.finer("Probe query: "+probePattern+" expecting: "+probe);
final Set<ObjectName> res = msc.queryNames(probePattern, null);
final ObjectName expected = probe;
PROBE_LOG.finer("Probe res: "+res);
if (res.contains(expected)) {
throw new IOException("namespace " +
targetNs + " is linking to itself: " +
"cycle detected by probe");
}
} catch (SecurityException x) {
PROBE_LOG.finer("Can't check for cycles: " + x);
// can't do anything....
} catch (RuntimeException x) {
PROBE_LOG.finer("Exception raised by queryNames: " + x);
throw x;
} finally {
probe = null;
}
} catch (MalformedObjectNameException x) {
final IOException io =
new IOException("invalid name space: probe failed");
io.initCause(x);
throw io;
}
PROBE_LOG.fine("Probe returned - no cycles");
probed = true;
}
// allows a Sun implementation JMX Namespace, such as the
// JMXRemoteNamespace, to control when a probe should be sent.
//
// XXX: TODO this probe thing is way too complex and fragile.
// This *must* go away or be replaced by something simpler.
// ideas are welcomed.
private boolean isProbeRequested(Object o) {
if (o instanceof DynamicProbe)
return ((DynamicProbe)o).isProbeRequested();
return false;
}
/**
* This method will send a probe to detect self-linking name spaces.
* A self linking namespace is a namespace that links back directly
* on itslef. Calling a method on such a name space always results
* in an infinite loop going through:
* [1]MBeanServer -> [2]NamespaceDispatcher -> [3]NamespaceInterceptor
* [4]JMXNamespace -> { network // or cd // or ... } -> [5]MBeanServer
* with exactly the same request than [1]...
*
* The namespace interceptor [2] tries to detect such condition the
* *first time* that the connection is used. It does so by setting
* a flag, and sending a queryNames() through the name space. If the
* queryNames comes back, it knows that there's a loop.
*
* The DynamicProbe interface can also be used by a Sun JMXNamespace
* implementation to request the emission of a probe at any time
* (see JMXRemoteNamespace implementation).
*/
private MBeanServer connection() {
try {
final MBeanServer c = super.source();
if (probe != null) // should not happen
throw new RuntimeException("connection is being probed");
if (probed == false || isProbeRequested(c)) {
try {
// Should not happen if class well behaved.
// Never probed. Force it.
//System.err.println("sending probe for " +
// "target="+targetNs+", source="+srcNs);
sendProbe(c);
} catch (IOException io) {
throw new RuntimeException(io.getMessage(), io);
}
}
if (c != null) {
return c;
}
} catch (RuntimeException x) {
throw x;
}
throw new NullPointerException("getMBeanServerConnection");
}
@Override
protected MBeanServer source() {
return connection();
}
@Override
protected MBeanServer getServerForLoading() {
// don't want to send probe on getClassLoader/getClassLoaderFor
return super.source();
}
/**
* Calls {@link MBeanServerConnection#queryNames queryNames}
* on the underlying
* {@link #getMBeanServerConnection MBeanServerConnection}.
**/
@Override
public final Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
// XXX: TODO this probe thing is way too complex and fragile.
// This *must* go away or be replaced by something simpler.
// ideas are welcomed.
PROBE_LOG.finer("probe is: "+probe+" pattern is: "+name);
if (probe != null && isProbePattern(name)) {
PROBE_LOG.finer("Return probe: "+probe);
return Collections.singleton(probe);
}
return super.queryNames(name, query);
}
@Override
protected ObjectName toSource(ObjectName targetName)
throws MalformedObjectNameException {
return proc.toSourceContext(targetName, true);
}
@Override
protected ObjectName toTarget(ObjectName sourceName)
throws MalformedObjectNameException {
return proc.toTargetContext(sourceName, false);
}
//
// Implements permission checks.
//
@Override
void check(ObjectName routingName, String member, String action) {
final SecurityManager sm = System.getSecurityManager();
if (sm == null) return;
if ("getDomains".equals(action)) return;
final JMXNamespacePermission perm =
new JMXNamespacePermission(serverName,member,
routingName,action);
sm.checkPermission(perm);
}
@Override
void checkCreate(ObjectName routingName, String className, String action) {
final SecurityManager sm = System.getSecurityManager();
if (sm == null) return;
final JMXNamespacePermission perm =
new JMXNamespacePermission(serverName,className,
routingName,action);
sm.checkPermission(perm);
}
//
// Implements permission filters for attributes...
//
@Override
AttributeList checkAttributes(ObjectName routingName,
AttributeList attributes, String action) {
check(routingName,null,action);
if (attributes == null || attributes.isEmpty()) return attributes;
final SecurityManager sm = System.getSecurityManager();
if (sm == null) return attributes;
final AttributeList res = new AttributeList();
for (Attribute at : attributes.asList()) {
try {
check(routingName,at.getName(),action);
res.add(at);
} catch (SecurityException x) { // DLS: OK
continue;
}
}
return res;
}
//
// Implements permission filters for attributes...
//
@Override
String[] checkAttributes(ObjectName routingName, String[] attributes,
String action) {
check(routingName,null,action);
if (attributes == null || attributes.length==0) return attributes;
final SecurityManager sm = System.getSecurityManager();
if (sm == null) return attributes;
final List<String> res = new ArrayList<String>(attributes.length);
for (String at : attributes) {
try {
check(routingName,at,action);
res.add(at);
} catch (SecurityException x) { // DLS: OK
continue;
}
}
return res.toArray(new String[res.size()]);
}
//
// Implements permission filters for domains...
//
@Override
String[] checkDomains(String[] domains, String action) {
// in principle, this method is never called because
// getDomains() will never be called - since there's
// no way that MBeanServer.getDomains() can be routed
// to a NamespaceInterceptor.
//
// This is also why there's no getDomains() in a
// JMXNamespacePermission...
//
return super.checkDomains(domains, action);
}
//
// Implements permission filters for queries...
//
@Override
boolean checkQuery(ObjectName routingName, String action) {
try {
check(routingName,null,action);
return true;
} catch (SecurityException x) { // DLS: OK
return false;
}
}
}

View file

@ -0,0 +1,191 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace;
import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
/**
* The ObjectNameRouter is used to rewrite routing object names.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
public class ObjectNameRouter {
private static final int NAMESPACE_SEPARATOR_LENGTH =
NAMESPACE_SEPARATOR.length();
final String targetPrefix;
final String sourcePrefix;
final int slen;
final int tlen;
final boolean identity;
public ObjectNameRouter(String targetDirName) {
this(targetDirName,null);
}
/** Creates a new instance of ObjectNameRouter */
public ObjectNameRouter(final String remove, final String add) {
this.targetPrefix = (remove==null?"":remove);
this.sourcePrefix = (add==null?"":add);
tlen = targetPrefix.length();
slen = sourcePrefix.length();
identity = targetPrefix.equals(sourcePrefix);
}
public final ObjectName toTargetContext(ObjectName sourceName,
boolean removeLeadingSeparators) {
if (sourceName == null) return null;
if (identity) return sourceName;
String srcDomain = sourceName.getDomain();
// if the ObjectName starts with // and removeLeadingSeparators is
// true, then recursively strip leading //.
// Otherwise, do not rewrite ObjectName.
//
if (srcDomain.startsWith(NAMESPACE_SEPARATOR)) {
if (!removeLeadingSeparators) return sourceName;
else srcDomain = normalizeDomain(srcDomain,true);
}
if (slen != 0) {
if (!srcDomain.startsWith(sourcePrefix) ||
!srcDomain.startsWith(NAMESPACE_SEPARATOR,slen))
throw new IllegalArgumentException(
"ObjectName does not start with expected prefix "
+ sourcePrefix + ": " +
String.valueOf(sourceName));
srcDomain = srcDomain.substring(slen+NAMESPACE_SEPARATOR_LENGTH);
}
final String targetDomain =
(tlen>0?targetPrefix+NAMESPACE_SEPARATOR+srcDomain:srcDomain);
try {
return sourceName.withDomain(targetDomain);
} catch (MalformedObjectNameException x) {
throw new IllegalArgumentException(String.valueOf(sourceName),x);
}
}
public final ObjectName toSourceContext(ObjectName targetName,
boolean removeLeadingSeparators) {
if (targetName == null) return null;
if (identity) return targetName;
String targetDomain = targetName.getDomain();
if (targetDomain.startsWith(NAMESPACE_SEPARATOR)) {
if (!removeLeadingSeparators) return targetName;
else targetDomain =
normalizeDomain(targetDomain,true);
}
if (tlen != 0) {
if (!targetDomain.startsWith(targetPrefix) ||
!targetDomain.startsWith(NAMESPACE_SEPARATOR,tlen))
throw new IllegalArgumentException(
"ObjectName does not start with expected prefix "
+ targetPrefix + ": " +
String.valueOf(targetName));
targetDomain = targetDomain.
substring(tlen+NAMESPACE_SEPARATOR_LENGTH);
}
final String sourceDomain =
(slen>0?sourcePrefix+NAMESPACE_SEPARATOR+targetDomain:
targetDomain);
try {
return targetName.withDomain(sourceDomain);
} catch (MalformedObjectNameException x) {
throw new IllegalArgumentException(String.valueOf(targetName),x);
}
}
public final ObjectInstance toTargetContext(ObjectInstance sourceMoi,
boolean removeLeadingSeparators) {
if (sourceMoi == null) return null;
if (identity) return sourceMoi;
return new ObjectInstance(
toTargetContext(sourceMoi.getObjectName(),
removeLeadingSeparators),
sourceMoi.getClassName());
}
/**
* Removes leading, trailing, or duplicate // in a name space path.
**/
public static String normalizeDomain(String domain,
boolean removeLeadingSep) {
return normalizeNamespacePath(domain,removeLeadingSep,false,true);
}
/**
* Removes leading, trailing, or duplicate // in a name space path.
**/
public static String normalizeNamespacePath(String namespacePath,
boolean removeLeadingSep,
boolean removeTrailingSep,
boolean endsWithDomain) {
if (namespacePath.equals(""))
return "";
final String[] components = namespacePath.split(NAMESPACE_SEPARATOR);
final StringBuilder b =
new StringBuilder(namespacePath.length()+NAMESPACE_SEPARATOR_LENGTH);
String sep = null;
if (!removeLeadingSep && namespacePath.startsWith(NAMESPACE_SEPARATOR))
b.append(NAMESPACE_SEPARATOR);
int count = 0;
for (int i=0; i<components.length; i++) {
final String n=components[i];
if (n.equals("")) continue;
if (n.startsWith("/")||n.endsWith("/")) {
// throw exception unless we're looking at the last domain
// part of the ObjectName
if (! (endsWithDomain && i==(components.length-1))) {
throw new IllegalArgumentException(n+
" is not a valid name space identifier");
} else {
// There's a dirty little corner case when the domain
// part (last item) is exactly '/' - in that case we must
// not append '//'
//
removeTrailingSep = removeTrailingSep || n.equals("/");
}
}
if (sep != null) b.append(sep);
b.append(n);
sep = NAMESPACE_SEPARATOR;
count++;
}
if (!removeTrailingSep && namespacePath.endsWith(NAMESPACE_SEPARATOR)
&& count > 0)
b.append(NAMESPACE_SEPARATOR);
return b.toString();
}
}

View file

@ -0,0 +1,132 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace;
import com.sun.jmx.defaults.JmxProperties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.MBeanServerConnection;
import javax.management.namespace.JMXNamespaces;
/**
* A RoutingConnectionProxy is an MBeanServerConnection proxy that proxies a
* source name space in a source MBeanServerConnection.
* It wraps a source MBeanServerConnection, and rewrites routing
* ObjectNames. It is used to implement
* {@code JMXNamespaces.narrowToNamespace(MBeanServerConnection)}.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
public class RoutingConnectionProxy
extends RoutingProxy<MBeanServerConnection> {
/**
* A logger for this class.
**/
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
/**
* Creates a new instance of RoutingConnectionProxy
*/
public RoutingConnectionProxy(MBeanServerConnection source,
String sourceDir) {
this(source,sourceDir,"",false);
}
/**
* Creates a new instance of RoutingConnectionProxy
*/
public RoutingConnectionProxy(MBeanServerConnection source,
String sourceDir,
String targetDir,
boolean forwardsContext) {
super(source,sourceDir,targetDir,forwardsContext);
if (LOG.isLoggable(Level.FINER))
LOG.finer("RoutingConnectionProxy for " + getSourceNamespace() +
" created");
}
@Override
public String toString() {
final String targetNs = getTargetNamespace();
final String sourceNs = getSourceNamespace();
String wrapped = String.valueOf(source());
if ("".equals(targetNs)) {
if (forwardsContext)
wrapped = "ClientContext.withDynamicContext("+wrapped+")";
return "JMXNamespaces.narrowToNamespace("+
wrapped+", \""+
sourceNs+"\")";
}
return this.getClass().getSimpleName()+"("+wrapped+", \""+
sourceNs+"\", \""+
targetNs+"\", "+forwardsContext+")";
}
public static MBeanServerConnection cd(MBeanServerConnection source,
String sourcePath) {
if (source == null) throw new IllegalArgumentException("null");
if (source.getClass().equals(RoutingConnectionProxy.class)) {
// cast is OK here, but findbugs complains unless we use class.cast
final RoutingConnectionProxy other =
RoutingConnectionProxy.class.cast(source);
final String target = other.getTargetNamespace();
// Avoid multiple layers of serialization.
//
// We construct a new proxy from the original source instead of
// stacking a new proxy on top of the old one.
// - that is we replace
// cd ( cd ( x, dir1), dir2);
// by
// cd (x, dir1//dir2);
//
// We can do this only when the source class is exactly
// NamespaceConnectionProxy.
//
if (target == null || target.equals("")) {
final String path =
JMXNamespaces.concat(other.getSourceNamespace(),
sourcePath);
return new RoutingConnectionProxy(other.source(),path,"",
other.forwardsContext);
}
// Note: we could do possibly something here - but it would involve
// removing part of targetDir, and possibly adding
// something to sourcePath.
// Too complex to bother! => simply default to stacking...
}
return new RoutingConnectionProxy(source,sourcePath);
}
}

View file

@ -0,0 +1,671 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace;
import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.mbeanserver.Util;
import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.JMRuntimeException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.management.RuntimeMBeanException;
import javax.management.RuntimeOperationsException;
/**
* A RoutingMBeanServerConnection wraps a MBeanServerConnection, defining
* abstract methods that can be implemented by subclasses to rewrite
* routing ObjectNames. It is used to implement
* HandlerInterceptors (wrapping JMXNamespace instances) and routing
* proxies (used to implement cd operations).
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
public abstract class RoutingMBeanServerConnection<T extends MBeanServerConnection>
implements MBeanServerConnection {
/**
* A logger for this class.
**/
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
/**
* Creates a new instance of RoutingMBeanServerConnection
*/
public RoutingMBeanServerConnection() {
}
/**
* Returns the wrapped source connection.
**/
protected abstract T source() throws IOException;
/**
* Converts a target ObjectName to a source ObjectName.
**/
protected abstract ObjectName toSource(ObjectName targetName)
throws MalformedObjectNameException;
/**
* Converts a source ObjectName to a target ObjectName.
**/
protected abstract ObjectName toTarget(ObjectName sourceName)
throws MalformedObjectNameException;
/**
* Can be overridden by subclasses to check the validity of a new
* ObjectName used in createMBean or registerMBean.
* This method is typically used by subclasses which might require
* special handling for "null";
**/
protected ObjectName newSourceMBeanName(ObjectName targetName)
throws MBeanRegistrationException {
try {
return toSource(targetName);
} catch (Exception x) {
throw new MBeanRegistrationException(x,"Illegal MBean Name");
}
}
// Calls toSource(), Wraps MalformedObjectNameException.
ObjectName toSourceOrRuntime(ObjectName targetName)
throws RuntimeOperationsException {
try {
return toSource(targetName);
} catch (MalformedObjectNameException x) {
final IllegalArgumentException x2 =
new IllegalArgumentException(String.valueOf(targetName),x);
final RuntimeOperationsException x3 =
new RuntimeOperationsException(x2);
throw x3;
}
}
// Wraps given exception if needed.
RuntimeException makeCompliantRuntimeException(Exception x) {
if (x instanceof SecurityException) return (SecurityException)x;
if (x instanceof JMRuntimeException) return (JMRuntimeException)x;
if (x instanceof RuntimeException)
return new RuntimeOperationsException((RuntimeException)x);
if (x instanceof IOException)
return Util.newRuntimeIOException((IOException)x);
// shouldn't come here...
final RuntimeException x2 = new UndeclaredThrowableException(x);
return new RuntimeOperationsException(x2);
}
/**
* This method is a hook to implement permission checking in subclasses.
* By default, this method does nothing and simply returns
* {@code attribute}.
*
* @param routingName The name of the MBean in the enclosing context.
* This is of the form {@code <namespace>//<ObjectName>}.
* @param attributes The list of attributes to check permission for.
* @param action one of "getAttribute" or "setAttribute"
* @return The list of attributes for which the callers has the
* appropriate {@link
* javax.management.namespace.JMXNamespacePermission}.
*/
String[] checkAttributes(ObjectName routingName,
String[] attributes, String action) {
check(routingName,null,action);
return attributes;
}
/**
* This method is a hook to implement permission checking in subclasses.
* By default, this method does nothing and simply returns
* {@code attribute}.
*
* @param routingName The name of the MBean in the enclosing context.
* This is of the form {@code <namespace>//<ObjectName>}.
* @param attributes The list of attributes to check permission for.
* @param action one of "getAttribute" or "setAttribute"
* @return The list of attributes for which the callers has the
* appropriate {@link
* javax.management.namespace.JMXNamespacePermission}.
*/
AttributeList checkAttributes(ObjectName routingName,
AttributeList attributes, String action) {
check(routingName,null,action);
return attributes;
}
// from MBeanServerConnection
public AttributeList getAttributes(ObjectName name, String[] attributes)
throws InstanceNotFoundException, ReflectionException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
final String[] authorized =
checkAttributes(name,attributes,"getAttribute");
final AttributeList attrList =
source().getAttributes(sourceName,authorized);
return attrList;
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
/**
* This method is a hook to implement permission checking in subclasses.
* By default, this method does nothing.
* A subclass may override this method and throw a {@link
* SecurityException} if the permission is denied.
*
* @param routingName The name of the MBean in the enclosing context.
* This is of the form {@code <namespace>//<ObjectName>}.
* @param member The {@link
* javax.management.namespace.JMXNamespacePermission#getMember member}
* name.
* @param action The {@link
* javax.management.namespace.JMXNamespacePermission#getActions action}
* name.
*/
void check(ObjectName routingName,
String member, String action) {
}
void checkPattern(ObjectName routingPattern,
String member, String action) {
// pattern is checked only at posteriori by checkQuery.
// checking it a priori usually doesn't work, because ObjectName.apply
// does not work between two patterns.
check(null,null,action);
}
void checkCreate(ObjectName routingName, String className,
String action) {
}
// from MBeanServerConnection
public Object invoke(ObjectName name, String operationName, Object[] params,
String[] signature)
throws InstanceNotFoundException, MBeanException, ReflectionException,
IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
check(name, operationName, "invoke");
final Object result =
source().invoke(sourceName,operationName,params,
signature);
return result;
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public void unregisterMBean(ObjectName name)
throws InstanceNotFoundException, MBeanRegistrationException,
IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
check(name, null, "unregisterMBean");
source().unregisterMBean(sourceName);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public MBeanInfo getMBeanInfo(ObjectName name)
throws InstanceNotFoundException, IntrospectionException,
ReflectionException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
check(name, null, "getMBeanInfo");
return source().getMBeanInfo(sourceName);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public ObjectInstance getObjectInstance(ObjectName name)
throws InstanceNotFoundException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
check(name, null, "getObjectInstance");
return processOutputInstance(
source().getObjectInstance(sourceName));
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public boolean isRegistered(ObjectName name) throws IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
return source().isRegistered(sourceName);
} catch (RuntimeMBeanException x) {
throw new RuntimeOperationsException(x.getTargetException());
} catch (RuntimeException x) {
throw makeCompliantRuntimeException(x);
}
}
// from MBeanServerConnection
public void setAttribute(ObjectName name, Attribute attribute)
throws InstanceNotFoundException, AttributeNotFoundException,
InvalidAttributeValueException, MBeanException,
ReflectionException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
check(name,
(attribute==null?null:attribute.getName()),
"setAttribute");
source().setAttribute(sourceName,attribute);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public ObjectInstance createMBean(String className,
ObjectName name, ObjectName loaderName,
Object[] params, String[] signature)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException, InstanceNotFoundException, IOException {
final ObjectName sourceName = newSourceMBeanName(name);
// Loader Name is already a sourceLoaderName.
final ObjectName sourceLoaderName = loaderName;
try {
checkCreate(name, className, "instantiate");
checkCreate(name, className, "registerMBean");
final ObjectInstance instance =
source().createMBean(className,sourceName,
sourceLoaderName,
params,signature);
return processOutputInstance(instance);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public ObjectInstance createMBean(String className, ObjectName name,
Object[] params, String[] signature)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException, IOException {
final ObjectName sourceName = newSourceMBeanName(name);
try {
checkCreate(name, className, "instantiate");
checkCreate(name, className, "registerMBean");
return processOutputInstance(source().createMBean(className,
sourceName,params,signature));
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException, InstanceNotFoundException, IOException {
final ObjectName sourceName = newSourceMBeanName(name);
// Loader Name is already a source Loader Name.
final ObjectName sourceLoaderName = loaderName;
try {
checkCreate(name, className, "instantiate");
checkCreate(name, className, "registerMBean");
return processOutputInstance(source().createMBean(className,
sourceName,sourceLoaderName));
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public ObjectInstance createMBean(String className, ObjectName name)
throws ReflectionException, InstanceAlreadyExistsException,
MBeanRegistrationException, MBeanException,
NotCompliantMBeanException, IOException {
final ObjectName sourceName = newSourceMBeanName(name);
try {
checkCreate(name, className, "instantiate");
checkCreate(name, className, "registerMBean");
return processOutputInstance(source().
createMBean(className,sourceName));
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public Object getAttribute(ObjectName name, String attribute)
throws MBeanException, AttributeNotFoundException,
InstanceNotFoundException, ReflectionException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
check(name, attribute, "getAttribute");
return source().getAttribute(sourceName,attribute);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public boolean isInstanceOf(ObjectName name, String className)
throws InstanceNotFoundException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
check(name, null, "isInstanceOf");
return source().isInstanceOf(sourceName,className);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public AttributeList setAttributes(ObjectName name, AttributeList attributes)
throws InstanceNotFoundException, ReflectionException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
final AttributeList authorized =
checkAttributes(name, attributes, "setAttribute");
return source().
setAttributes(sourceName,authorized);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// Return names in the target's context.
Set<ObjectInstance> processOutputInstances(Set<ObjectInstance> sources) {
final Set<ObjectInstance> result = Util.equivalentEmptySet(sources);
for (ObjectInstance i : sources) {
try {
final ObjectInstance target = processOutputInstance(i);
if (!checkQuery(target.getObjectName(), "queryMBeans"))
continue;
result.add(target);
} catch (Exception x) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Skiping returned item: " +
"Unexpected exception while processing " +
"ObjectInstance: " + x);
}
continue;
}
}
return result;
}
/**
* This is a hook to implement permission checking in subclasses.
*
* Checks that the caller has sufficient permission for returning
* information about {@code sourceName} in {@code action}.
*
* By default always return true. Subclass may override this method
* and return false if the caller doesn't have sufficient permissions.
*
* @param routingName The name of the MBean to include or exclude from
* the query, expressed in the enclosing context.
* This is of the form {@code <namespace>//<ObjectName>}.
* @param action one of "queryNames" or "queryMBeans"
* @return true if {@code sourceName} can be returned.
*/
boolean checkQuery(ObjectName routingName, String action) {
return true;
}
// Return names in the target's context.
ObjectInstance processOutputInstance(ObjectInstance source) {
if (source == null) return null;
final ObjectName sourceName = source.getObjectName();
try {
final ObjectName targetName = toTarget(sourceName);
return new ObjectInstance(targetName,source.getClassName());
} catch (MalformedObjectNameException x) {
final IllegalArgumentException x2 =
new IllegalArgumentException(String.valueOf(sourceName),x);
final RuntimeOperationsException x3 =
new RuntimeOperationsException(x2);
throw x3;
}
}
// Returns names in the target's context.
Set<ObjectName> processOutputNames(Set<ObjectName> sourceNames) {
final Set<ObjectName> names = Util.equivalentEmptySet(sourceNames);
for (ObjectName n : sourceNames) {
try {
final ObjectName targetName = toTarget(n);
if (!checkQuery(targetName, "queryNames")) continue;
names.add(targetName);
} catch (Exception x) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Skiping returned item: " +
"Unexpected exception while processing " +
"ObjectInstance: " + x);
}
continue;
}
}
return names;
}
// from MBeanServerConnection
public Set<ObjectInstance> queryMBeans(ObjectName name,
QueryExp query) throws IOException {
if (name == null) name=ObjectName.WILDCARD;
final ObjectName sourceName = toSourceOrRuntime(name);
try {
checkPattern(name,null,"queryMBeans");
return processOutputInstances(
source().queryMBeans(sourceName,query));
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public Set<ObjectName> queryNames(ObjectName name, QueryExp query)
throws IOException {
if (name == null) name=ObjectName.WILDCARD;
final ObjectName sourceName = toSourceOrRuntime(name);
try {
checkPattern(name,null,"queryNames");
final Set<ObjectName> tmp = source().queryNames(sourceName,query);
final Set<ObjectName> out = processOutputNames(tmp);
//System.err.println("queryNames: out: "+out);
return out;
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public void removeNotificationListener(ObjectName name,
NotificationListener listener)
throws InstanceNotFoundException,
ListenerNotFoundException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
check(name,null,"removeNotificationListener");
source().removeNotificationListener(sourceName,listener);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public void addNotificationListener(ObjectName name, ObjectName listener,
NotificationFilter filter, Object handback)
throws InstanceNotFoundException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
// Listener name is already a source listener name.
try {
check(name,null,"addNotificationListener");
source().addNotificationListener(sourceName,listener,
filter,handback);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public void addNotificationListener(ObjectName name,
NotificationListener listener, NotificationFilter filter,
Object handback) throws InstanceNotFoundException, IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
check(name,null,"addNotificationListener");
source().addNotificationListener(sourceName, listener, filter,
handback);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public void removeNotificationListener(ObjectName name,
NotificationListener listener, NotificationFilter filter,
Object handback)
throws InstanceNotFoundException, ListenerNotFoundException,
IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
check(name,null,"removeNotificationListener");
source().removeNotificationListener(sourceName,listener,filter,
handback);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public void removeNotificationListener(ObjectName name, ObjectName listener,
NotificationFilter filter, Object handback)
throws InstanceNotFoundException, ListenerNotFoundException,
IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
check(name,null,"removeNotificationListener");
source().removeNotificationListener(sourceName,listener,
filter,handback);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public void removeNotificationListener(ObjectName name, ObjectName listener)
throws InstanceNotFoundException, ListenerNotFoundException,
IOException {
final ObjectName sourceName = toSourceOrRuntime(name);
// listener name is already a source name...
final ObjectName sourceListener = listener;
try {
check(name,null,"removeNotificationListener");
source().removeNotificationListener(sourceName,sourceListener);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public Integer getMBeanCount() throws IOException {
try {
return source().getMBeanCount();
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// from MBeanServerConnection
public String[] getDomains() throws IOException {
try {
check(null,null,"getDomains");
final String[] domains = source().getDomains();
return checkDomains(domains,"getDomains");
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
/**
* This method is a hook to implement permission checking in subclasses.
* Checks that the caller as the necessary permissions to view the
* given domain. If not remove the domains for which the caller doesn't
* have permission from the list.
* <p>
* By default, this method always returns {@code domains}
*
* @param domains The domains to return.
* @param action "getDomains"
* @return a filtered list of domains.
*/
String[] checkDomains(String[] domains, String action) {
return domains;
}
// from MBeanServerConnection
public String getDefaultDomain() throws IOException {
try {
return source().getDefaultDomain();
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
}

View file

@ -0,0 +1,282 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace;
import com.sun.jmx.defaults.JmxProperties;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.namespace.JMXNamespaces;
/**
* An RoutingProxy narrows on a given name space in a
* source object implementing MBeanServerConnection.
* It is used to implement
* {@code JMXNamespaces.narrowToNamespace(...)}.
* This abstract class has two concrete subclasses:
* <p>{@link RoutingConnectionProxy}: to cd in an MBeanServerConnection.</p>
* <p>{@link RoutingServerProxy}: to cd in an MBeanServer.</p>
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
public abstract class RoutingProxy<T extends MBeanServerConnection>
extends RoutingMBeanServerConnection<T> {
/**
* A logger for this class.
**/
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
// The source MBeanServerConnection
private final T source;
// The name space we're narrowing to (usually some name space in
// the source MBeanServerConnection
private final String sourceNs;
// The name space we pretend to be mounted in (usually "")
private final String targetNs;
// The name of the JMXNamespace that handles the source name space
private final ObjectName handlerName;
private final ObjectNameRouter router;
final boolean forwardsContext;
private volatile String defaultDomain = null;
/**
* Creates a new instance of RoutingProxy
*/
protected RoutingProxy(T source,
String sourceNs,
String targetNs,
boolean forwardsContext) {
if (source == null) throw new IllegalArgumentException("null");
this.sourceNs = JMXNamespaces.normalizeNamespaceName(sourceNs);
// Usually sourceNs is not null, except when implementing
// Client Contexts
//
if (sourceNs.equals("")) {
this.handlerName = null;
} else {
// System.err.println("sourceNs: "+sourceNs);
this.handlerName =
JMXNamespaces.getNamespaceObjectName(this.sourceNs);
try {
// System.err.println("handlerName: "+handlerName);
if (!source.isRegistered(handlerName))
throw new IllegalArgumentException(sourceNs +
": no such name space");
} catch (IOException x) {
throw new IllegalArgumentException("source stale: "+x,x);
}
}
this.source = source;
this.targetNs = (targetNs==null?"":
JMXNamespaces.normalizeNamespaceName(targetNs));
this.router =
new ObjectNameRouter(this.targetNs,this.sourceNs);
this.forwardsContext = forwardsContext;
if (LOG.isLoggable(Level.FINER))
LOG.finer("RoutingProxy for " + this.sourceNs + " created");
}
@Override
public T source() { return source; }
ObjectNameRouter getObjectNameRouter() {
// TODO: uncomment this when contexts are added
// if (forwardsContext)
// return ObjectNameRouter.wrapWithContext(router);
// else
return router;
}
@Override
public ObjectName toSource(ObjectName targetName)
throws MalformedObjectNameException {
if (targetName == null) return null;
if (targetName.getDomain().equals("") && targetNs.equals("")) {
try {
if (defaultDomain == null)
defaultDomain = getDefaultDomain();
} catch(Exception x) {
LOG.log(Level.FINEST,"Failed to get default domain",x);
}
if (defaultDomain != null)
targetName = targetName.withDomain(defaultDomain);
}
final ObjectNameRouter r = getObjectNameRouter();
return r.toSourceContext(targetName,true);
}
@Override
protected ObjectName newSourceMBeanName(ObjectName targetName)
throws MBeanRegistrationException {
if (targetName != null) return super.newSourceMBeanName(targetName);
// OK => we can accept null if sourceNs is empty.
if (sourceNs.equals("")) return null;
throw new MBeanRegistrationException(
new IllegalArgumentException(
"Can't use null ObjectName with namespaces"));
}
@Override
public ObjectName toTarget(ObjectName sourceName)
throws MalformedObjectNameException {
if (sourceName == null) return null;
final ObjectNameRouter r = getObjectNameRouter();
return r.toTargetContext(sourceName,false);
}
private Object getAttributeFromHandler(String attributeName)
throws IOException {
try {
return source().getAttribute(handlerName,attributeName);
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
} catch (IOException x) {
throw x;
} catch (MBeanException ex) {
throw new IOException("Failed to get "+attributeName+": "+
ex.getMessage(),
ex.getTargetException());
} catch (AttributeNotFoundException ex) {
throw new IOException("Failed to get "+attributeName+": "+
ex.getMessage(),ex);
} catch (InstanceNotFoundException ex) {
throw new IOException("Failed to get "+attributeName+": "+
ex.getMessage(),ex);
} catch (ReflectionException ex) {
throw new IOException("Failed to get "+attributeName+": "+
ex.getMessage(),ex);
}
}
// We cannot call getMBeanCount() on the underlying
// MBeanServerConnection, because it would return the number of
// 'top-level' MBeans, not the number of MBeans in the name space
// we are narrowing to. Instead we're calling getMBeanCount() on
// the JMXNamespace that handles the source name space.
//
// There is however one particular case when the sourceNs is empty.
// In that case, there's no handler - and the 'source' is the top
// level namespace. In that particular case, handlerName will be null,
// and we directly invoke the top level source().
// This later complex case is only used when implementing ClientContexts.
//
@Override
public Integer getMBeanCount() throws IOException {
try {
if (handlerName == null) return source().getMBeanCount();
return (Integer) getAttributeFromHandler("MBeanCount");
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// We cannot call getDomains() on the underlying
// MBeanServerConnection, because it would return the domains of
// 'top-level' MBeans, not the domains of MBeans in the name space
// we are narrowing to. Instead we're calling getDomains() on
// the JMXNamespace that handles the source name space.
//
// There is however one particular case when the sourceNs is empty.
// In that case, there's no handler - and the 'source' is the top
// level namespace. In that particular case, handlerName will be null,
// and we directly invoke the top level source().
// This later complex case is only used when implementing ClientContexts.
//
@Override
public String[] getDomains() throws IOException {
try {
if (handlerName == null) return source().getDomains();
return (String[]) getAttributeFromHandler("Domains");
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
// We cannot call getDefaultDomain() on the underlying
// MBeanServerConnection, because it would return the default domain of
// 'top-level' namespace, not the default domain in the name space
// we are narrowing to. Instead we're calling getDefaultDomain() on
// the JMXNamespace that handles the source name space.
//
// There is however one particular case when the sourceNs is empty.
// In that case, there's no handler - and the 'source' is the top
// level namespace. In that particular case, handlerName will be null,
// and we directly invoke the top level source().
// This later complex case is only used when implementing ClientContexts.
//
@Override
public String getDefaultDomain() throws IOException {
try {
if (handlerName == null) {
defaultDomain = source().getDefaultDomain();
} else {
defaultDomain =(String)
getAttributeFromHandler("DefaultDomain");
}
return defaultDomain;
} catch (RuntimeException ex) {
throw makeCompliantRuntimeException(ex);
}
}
public String getSourceNamespace() {
return sourceNs;
}
public String getTargetNamespace() {
return targetNs;
}
@Override
public String toString() {
return super.toString()+", sourceNs="+
sourceNs + (targetNs.equals("")?"":
(" mounted on targetNs="+targetNs));
}
}

View file

@ -0,0 +1,602 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace;
import com.sun.jmx.mbeanserver.Util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Collections;
import java.util.Set;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.OperationsException;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.management.loading.ClassLoaderRepository;
import javax.management.namespace.JMXNamespaces;
/**
* A RoutingServerProxy is an MBeanServer proxy that proxies a
* source name space in a source MBeanServer.
* It wraps a source MBeanServer, and rewrites routing ObjectNames.
* It is typically use for implementing 'cd' operations, and
* will add the source name space to routing ObjectNames at input,
* and remove it at output.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
*
* @since 1.7
*/
public class RoutingServerProxy
extends RoutingProxy<MBeanServer>
implements MBeanServer {
/**
* Creates a new instance of RoutingServerProxy
*/
public RoutingServerProxy(MBeanServer source,
String sourceNs) {
this(source,sourceNs,"",false);
}
public RoutingServerProxy(MBeanServer source,
String sourceNs,
String targetNs,
boolean forwardsContext) {
super(source,sourceNs,targetNs,forwardsContext);
}
/**
* This method is called each time an IOException is raised when
* trying to forward an operation to the underlying
* MBeanServerConnection, as a result of calling
* {@link #getMBeanServerConnection()} or as a result of invoking the
* operation on the returned connection.
* Subclasses may redefine this method if they need to perform any
* specific handling of IOException (logging etc...).
* @param x The raised IOException.
* @param method The name of the method in which the exception was
* raised. This is one of the methods of the MBeanServer
* interface.
* @return A RuntimeException that should be thrown by the caller.
* In this default implementation, this is an
* {@link UndeclaredThrowableException} wrapping <var>x</var>.
**/
protected RuntimeException handleIOException(IOException x,
String method) {
return Util.newRuntimeIOException(x);
}
//--------------------------------------------
//--------------------------------------------
//
// Implementation of the MBeanServer interface
//
//--------------------------------------------
//--------------------------------------------
@Override
public void addNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException {
try {
super.addNotificationListener(name, listener,
filter, handback);
} catch (IOException x) {
throw handleIOException(x,"addNotificationListener");
}
}
@Override
public void addNotificationListener(ObjectName name,
ObjectName listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException {
try {
super.addNotificationListener(name, listener,
filter, handback);
} catch (IOException x) {
throw handleIOException(x,"addNotificationListener");
}
}
@Override
public ObjectInstance createMBean(String className, ObjectName name)
throws
ReflectionException,
InstanceAlreadyExistsException,
MBeanRegistrationException,
MBeanException,
NotCompliantMBeanException {
try {
return super.createMBean(className, name);
} catch (IOException x) {
throw handleIOException(x,"createMBean");
}
}
@Override
public ObjectInstance createMBean(String className, ObjectName name,
Object params[], String signature[])
throws
ReflectionException,
InstanceAlreadyExistsException,
MBeanRegistrationException,
MBeanException,
NotCompliantMBeanException {
try {
return super.createMBean(className, name,
params, signature);
} catch (IOException x) {
throw handleIOException(x,"createMBean");
}
}
@Override
public ObjectInstance createMBean(String className,
ObjectName name,
ObjectName loaderName)
throws
ReflectionException,
InstanceAlreadyExistsException,
MBeanRegistrationException,
MBeanException,
NotCompliantMBeanException,
InstanceNotFoundException {
try {
return super.createMBean(className, name, loaderName);
} catch (IOException x) {
throw handleIOException(x,"createMBean");
}
}
@Override
public ObjectInstance createMBean(String className,
ObjectName name,
ObjectName loaderName,
Object params[],
String signature[])
throws
ReflectionException,
InstanceAlreadyExistsException,
MBeanRegistrationException,
MBeanException,
NotCompliantMBeanException,
InstanceNotFoundException {
try {
return super.createMBean(className, name, loaderName,
params, signature);
} catch (IOException x) {
throw handleIOException(x,"createMBean");
}
}
/**
* @deprecated see {@link MBeanServer#deserialize(ObjectName,byte[])
* MBeanServer}
**/
@Deprecated
public ObjectInputStream deserialize(ObjectName name, byte[] data)
throws InstanceNotFoundException, OperationsException {
final ObjectName sourceName = toSourceOrRuntime(name);
try {
return source().deserialize(sourceName,data);
} catch (RuntimeException x) {
throw makeCompliantRuntimeException(x);
}
}
/**
* @deprecated see {@link MBeanServer#deserialize(String,byte[])
* MBeanServer}
*/
@Deprecated
public ObjectInputStream deserialize(String className, byte[] data)
throws OperationsException, ReflectionException {
try {
return source().deserialize(className,data);
} catch (RuntimeException x) {
throw makeCompliantRuntimeException(x);
}
}
/**
* @deprecated see {@link MBeanServer#deserialize(String,ObjectName,byte[])
* MBeanServer}
*/
@Deprecated
public ObjectInputStream deserialize(String className,
ObjectName loaderName,
byte[] data)
throws
InstanceNotFoundException,
OperationsException,
ReflectionException {
try {
return source().deserialize(className,loaderName,data);
} catch (RuntimeException x) {
throw makeCompliantRuntimeException(x);
}
}
@Override
public Object getAttribute(ObjectName name, String attribute)
throws
MBeanException,
AttributeNotFoundException,
InstanceNotFoundException,
ReflectionException {
try {
return super.getAttribute(name, attribute);
} catch (IOException x) {
throw handleIOException(x,"getAttribute");
}
}
@Override
public AttributeList getAttributes(ObjectName name, String[] attributes)
throws InstanceNotFoundException, ReflectionException {
try {
return super.getAttributes(name, attributes);
} catch (IOException x) {
throw handleIOException(x,"getAttributes");
}
}
public ClassLoader getClassLoader(ObjectName loaderName)
throws InstanceNotFoundException {
final ObjectName sourceName = toSourceOrRuntime(loaderName);
try {
return source().getClassLoader(sourceName);
} catch (RuntimeException x) {
throw makeCompliantRuntimeException(x);
}
}
public ClassLoader getClassLoaderFor(ObjectName mbeanName)
throws InstanceNotFoundException {
final ObjectName sourceName = toSourceOrRuntime(mbeanName);
try {
return source().getClassLoaderFor(sourceName);
} catch (RuntimeException x) {
throw makeCompliantRuntimeException(x);
}
}
public ClassLoaderRepository getClassLoaderRepository() {
try {
return source().getClassLoaderRepository();
} catch (RuntimeException x) {
throw makeCompliantRuntimeException(x);
}
}
@Override
public String getDefaultDomain() {
try {
return super.getDefaultDomain();
} catch (IOException x) {
throw handleIOException(x,"getDefaultDomain");
}
}
@Override
public String[] getDomains() {
try {
return super.getDomains();
} catch (IOException x) {
throw handleIOException(x,"getDomains");
}
}
@Override
public Integer getMBeanCount() {
try {
return super.getMBeanCount();
} catch (IOException x) {
throw handleIOException(x,"getMBeanCount");
}
}
@Override
public MBeanInfo getMBeanInfo(ObjectName name)
throws
InstanceNotFoundException,
IntrospectionException,
ReflectionException {
try {
return super.getMBeanInfo(name);
} catch (IOException x) {
throw handleIOException(x,"getMBeanInfo");
}
}
@Override
public ObjectInstance getObjectInstance(ObjectName name)
throws InstanceNotFoundException {
try {
return super.getObjectInstance(name);
} catch (IOException x) {
throw handleIOException(x,"getObjectInstance");
}
}
public Object instantiate(String className)
throws ReflectionException, MBeanException {
try {
return source().instantiate(className);
} catch (RuntimeException x) {
throw makeCompliantRuntimeException(x);
}
}
public Object instantiate(String className,
Object params[],
String signature[])
throws ReflectionException, MBeanException {
try {
return source().instantiate(className,
params,signature);
} catch (RuntimeException x) {
throw makeCompliantRuntimeException(x);
}
}
public Object instantiate(String className, ObjectName loaderName)
throws ReflectionException, MBeanException,
InstanceNotFoundException {
final ObjectName srcLoaderName = toSourceOrRuntime(loaderName);
try {
return source().instantiate(className,srcLoaderName);
} catch (RuntimeException x) {
throw makeCompliantRuntimeException(x);
}
}
public Object instantiate(String className, ObjectName loaderName,
Object params[], String signature[])
throws ReflectionException, MBeanException,
InstanceNotFoundException {
final ObjectName srcLoaderName = toSourceOrRuntime(loaderName);
try {
return source().instantiate(className,srcLoaderName,
params,signature);
} catch (RuntimeException x) {
throw makeCompliantRuntimeException(x);
}
}
@Override
public Object invoke(ObjectName name, String operationName,
Object params[], String signature[])
throws
InstanceNotFoundException,
MBeanException,
ReflectionException {
try {
return super.invoke(name,operationName,params,signature);
} catch (IOException x) {
throw handleIOException(x,"invoke");
}
}
@Override
public boolean isInstanceOf(ObjectName name, String className)
throws InstanceNotFoundException {
try {
return super.isInstanceOf(name, className);
} catch (IOException x) {
throw handleIOException(x,"isInstanceOf");
}
}
@Override
public boolean isRegistered(ObjectName name) {
try {
return super.isRegistered(name);
} catch (IOException x) {
throw handleIOException(x,"isRegistered");
}
}
@Override
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
try {
return super.queryMBeans(name, query);
} catch (IOException x) {
handleIOException(x,"queryMBeans");
return Collections.emptySet();
}
}
@Override
public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
try {
return super.queryNames(name, query);
} catch (IOException x) {
handleIOException(x,"queryNames");
return Collections.emptySet();
}
}
public ObjectInstance registerMBean(Object object, ObjectName name)
throws
InstanceAlreadyExistsException,
MBeanRegistrationException,
NotCompliantMBeanException {
final ObjectName sourceName = newSourceMBeanName(name);
try {
return processOutputInstance(
source().registerMBean(object,sourceName));
} catch (RuntimeException x) {
throw makeCompliantRuntimeException(x);
}
}
@Override
public void removeNotificationListener(ObjectName name,
NotificationListener listener)
throws InstanceNotFoundException, ListenerNotFoundException {
try {
super.removeNotificationListener(name, listener);
} catch (IOException x) {
throw handleIOException(x,"removeNotificationListener");
}
}
@Override
public void removeNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException, ListenerNotFoundException {
try {
super.removeNotificationListener(name, listener,
filter, handback);
} catch (IOException x) {
throw handleIOException(x,"removeNotificationListener");
}
}
@Override
public void removeNotificationListener(ObjectName name,
ObjectName listener)
throws InstanceNotFoundException, ListenerNotFoundException {
try {
super.removeNotificationListener(name, listener);
} catch (IOException x) {
throw handleIOException(x,"removeNotificationListener");
}
}
@Override
public void removeNotificationListener(ObjectName name,
ObjectName listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException, ListenerNotFoundException {
try {
super.removeNotificationListener(name, listener,
filter, handback);
} catch (IOException x) {
throw handleIOException(x,"removeNotificationListener");
}
}
@Override
public void setAttribute(ObjectName name, Attribute attribute)
throws
InstanceNotFoundException,
AttributeNotFoundException,
InvalidAttributeValueException,
MBeanException,
ReflectionException {
try {
super.setAttribute(name, attribute);
} catch (IOException x) {
throw handleIOException(x,"setAttribute");
}
}
@Override
public AttributeList setAttributes(ObjectName name,
AttributeList attributes)
throws InstanceNotFoundException, ReflectionException {
try {
return super.setAttributes(name, attributes);
} catch (IOException x) {
throw handleIOException(x,"setAttributes");
}
}
@Override
public void unregisterMBean(ObjectName name)
throws InstanceNotFoundException, MBeanRegistrationException {
try {
super.unregisterMBean(name);
} catch (IOException x) {
throw handleIOException(x,"unregisterMBean");
}
}
public static MBeanServer cd(MBeanServer source, String sourcePath) {
if (source == null) throw new IllegalArgumentException("null");
if (source.getClass().equals(RoutingServerProxy.class)) {
// cast is OK here, but findbugs complains unless we use class.cast
final RoutingServerProxy other =
RoutingServerProxy.class.cast(source);
final String target = other.getTargetNamespace();
// Avoid multiple layers of serialization.
//
// We construct a new proxy from the original source instead of
// stacking a new proxy on top of the old one.
// - that is we replace
// cd ( cd ( x, dir1), dir2);
// by
// cd (x, dir1//dir2);
//
// We can do this only when the source class is exactly
// NamespaceServerProxy.
//
if (target == null || target.equals("")) {
final String path =
JMXNamespaces.concat(other.getSourceNamespace(),
sourcePath);
return new RoutingServerProxy(other.source(),path,"",
other.forwardsContext);
}
// Note: we could do possibly something here - but it would involve
// removing part of targetDir, and possibly adding
// something to sourcePath.
// Too complex to bother! => simply default to stacking...
}
return new RoutingServerProxy(source,sourcePath);
}
}

View file

@ -0,0 +1,45 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>The <code>com.sun.jmx.namespace</code> package</title>
<!--
Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 only, as
published by the Free Software Foundation. Sun designates this
particular file as subject to the "Classpath" exception as provided
by Sun in the LICENSE file that accompanied this code.
This code 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
version 2 for more details (a copy is included in the LICENSE file that
accompanied this code).
You should have received a copy of the GNU General Public License version
2 along with this work; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
CA 95054 USA or visit www.sun.com if you need additional information or
have any questions.
-->
</head>
<body bgcolor="white">
<p>The <code>com.sun.jmx.namespace</code> package contains
sun specific implementation classes used to implement the
JMX namespaces.
</p>
<p><b>DO NOT USE THESE CLASSES DIRECTLY</b></p>
<p><b>
This API is a Sun internal API and is subject to changes without notice.
</b></p>
<p>The public API through wich these proprietary classes can be
invoked is located in <code>javax.management.namespace</code>
package.
</p>
</body>
</html>

View file

@ -0,0 +1,150 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace.serial;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
/**
* Class DefaultRewritingProcessor. Rewrite ObjectName in input & output
* parameters.
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
// We know that rewriting using serialization is costly.
// This object tries to determine whether an object needs rewriting prior
// to rewriting, and rewrites by creating a new object in those cases
// where we know how to recreate a new object (e.g. a Notification).
// Rewriting is however usually not used - so this object is just a
// skeleton that eventually uses serialization...
//
class DefaultRewritingProcessor extends RewritingProcessor {
private static enum RewriteMode {
INPUT, // Input from target to source (parameters)
OUTPUT // Output from source to target (results)
};
private final boolean identity;
public DefaultRewritingProcessor(String targetDirName) {
this(targetDirName,null);
}
/** Creates a new instance of SerialParamProcessor */
public DefaultRewritingProcessor(final String remove, final String add) {
super(new SerialRewritingProcessor(remove, add));
identity = remove.equals(add);
}
private ObjectName rewriteObjectName(RewriteMode mode,
ObjectName name) {
return changeContext(mode, name);
}
private ObjectInstance rewriteObjectInstance(RewriteMode mode,
ObjectInstance moi) {
final ObjectName srcName = moi.getObjectName();
final ObjectName targetName = changeContext(mode,srcName);
if (targetName == srcName) return moi;
return new ObjectInstance(targetName,moi.getClassName());
}
private Object processObject(RewriteMode mode, Object obj) {
if (obj == null) return null;
// Some things which will always needs rewriting:
// ObjectName, ObjectInstance, and Notifications.
// Take care of those we can handle here...
//
if (obj instanceof ObjectName)
return rewriteObjectName(mode,(ObjectName) obj);
else if (obj instanceof ObjectInstance)
return rewriteObjectInstance(mode,(ObjectInstance) obj);
// TODO: add other standard JMX classes - like e.g. MBeanInfo...
//
// Well, the object may contain an ObjectName => pass it to
// our serial rewriting delegate...
//
return processAnyObject(mode,obj);
}
private Object processAnyObject(RewriteMode mode, Object obj) {
switch (mode) {
case INPUT:
return super.rewriteInput(obj);
case OUTPUT:
return super.rewriteOutput(obj);
default: // can't happen.
throw new AssertionError();
}
}
private ObjectName changeContext(RewriteMode mode, ObjectName name) {
switch (mode) {
case INPUT:
return toSourceContext(name);
case OUTPUT:
return toTargetContext(name);
default: // can't happen.
throw new AssertionError();
}
}
@Override
public ObjectName toTargetContext(ObjectName srcName) {
if (identity) return srcName;
return super.toTargetContext(srcName);
}
@Override
public ObjectName toSourceContext(ObjectName targetName) {
if (identity) return targetName;
return super.toSourceContext(targetName);
}
@SuppressWarnings("unchecked")
@Override
public <T> T rewriteInput(T input) {
if (identity) return input;
return (T) processObject(RewriteMode.INPUT,input);
}
@SuppressWarnings("unchecked")
@Override
public <T> T rewriteOutput(T result) {
if (identity) return result;
return (T) processObject(RewriteMode.OUTPUT,result);
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace.serial;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
/**
* Class RoutingOnlyProcessor. A RewritingProcessor that uses
* Java Serialization to rewrite ObjectNames contained in
* input & results...
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
*
* @since 1.7
*/
class IdentityProcessor extends RewritingProcessor {
/** Creates a new instance of SerialRewritingProcessor */
public IdentityProcessor() {
}
@Override
public <T> T rewriteOutput(T result) {
return result;
}
@Override
public <T> T rewriteInput(T input) {
return input;
}
@Override
public final ObjectName toTargetContext(ObjectName sourceName) {
return sourceName;
}
@Override
public final ObjectInstance toTargetContext(ObjectInstance sourceMoi) {
return sourceMoi;
}
@Override
public final ObjectName toSourceContext(ObjectName targetName) {
return targetName;
}
}

View file

@ -0,0 +1,145 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace.serial;
import com.sun.jmx.defaults.JmxProperties;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The JMXNamespaceContext class is used to implement a thread local
* serialization / deserialization context for namespaces.
* <p>
* This class is consulted by {@link javax.management.ObjectName} at
* serialization / deserialization time.
* The serialization or deserialization context is established by
* by the {@link SerialRewritingProcessor} defined in this package.
* <p>
* These classes are Sun proprietary APIs, subject to change without
* notice. Do not use these classes directly.
* The public API to rewrite ObjectNames embedded in parameters is
* defined in {@link javax.management.namespace.JMXNamespaces}.
*
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
public class JMXNamespaceContext {
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
public final String prefixToRemove;
public final String prefixToAdd;
private JMXNamespaceContext(String add, String remove) {
prefixToRemove = (remove==null?"":remove);
prefixToAdd = (add==null?"":add);
}
private final static class SerialContext {
private JMXNamespaceContext serializationContext;
private JMXNamespaceContext deserializationContext;
public SerialContext(){
serializationContext = new JMXNamespaceContext("","");
deserializationContext = new JMXNamespaceContext("","");
}
}
private final static ThreadLocal<SerialContext> prefix =
new ThreadLocal<SerialContext>() {
@Override
protected SerialContext initialValue() {
return new SerialContext();
}
};
public static JMXNamespaceContext getSerializationContext() {
return prefix.get().serializationContext;
}
public static JMXNamespaceContext getDeserializationContext() {
return prefix.get().deserializationContext;
}
private static String[] setSerializationContext(String oldPrefix,
String newPrefix) {
final SerialContext c = prefix.get();
JMXNamespaceContext dc = c.serializationContext;
String[] old = {dc.prefixToRemove, dc.prefixToAdd};
c.serializationContext = new JMXNamespaceContext(newPrefix,oldPrefix);
return old;
}
private static String[] setDeserializationContext(String oldPrefix,
String newPrefix) {
final SerialContext c = prefix.get();
JMXNamespaceContext dc = c.deserializationContext;
String[] old = {dc.prefixToRemove, dc.prefixToAdd};
c.deserializationContext = new JMXNamespaceContext(newPrefix,oldPrefix);
return old;
}
static void serialize(ObjectOutputStream stream, Object obj,
String prefixToRemove, String prefixToAdd)
throws IOException {
final String[] old =
setSerializationContext(prefixToRemove,prefixToAdd);
try {
stream.writeObject(obj);
} finally {
try {
setSerializationContext(old[0],old[1]);
} catch (Exception x) {
LOG.log(Level.FINEST,
"failed to restore serialization context",x);
}
}
}
static Object deserialize(ObjectInputStream stream,
String prefixToRemove,
String prefixToAdd)
throws IOException, ClassNotFoundException {
final String[] old =
setDeserializationContext(prefixToRemove,prefixToAdd);
try {
return stream.readObject();
} finally {
try {
setDeserializationContext(old[0],old[1]);
} catch (Exception x) {
LOG.log(Level.FINEST,
"failed to restore serialization context",x);
}
}
}
}

View file

@ -0,0 +1,362 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace.serial;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
/**
* An object that can rewrite ObjectNames contained in input/output
* parameters when entering/leaving a {@link javax.management.namespace
* namespace}.
* <p>When entering a {@link javax.management.namespace
* namespace}, the {@code namespace} prefix is stripped from
* ObjectNames contained in input parameters. When leaving a
* {@code namespace},
* the {@code namespace} prefix is prepended to the ObjectNames contained in
* the result parameters returned from that {@code namespace}.
* </p>
* <p>Objects that need to perform these operations usually use a
* {@code RewritingProcessor} for that purpose.<br>
* The {@code RewritingProcessor} allows a somewhat larger
* transformation in which part of a prefix {@link #newRewritingProcessor
* remove} can be replaced by another prefix {@link #newRewritingProcessor
* add}. The transformation described above correspond to the case where
* {@code remove} is the stripped {@link javax.management.namespace
* namespace} prefix (removed when entering the {@code namespace}) and
* {@code add} is the empty String {@code ""}.
* <br>
* It is interesting to note that {@link
* javax.management.JMXNamespaces#narrowToNamespace narrowToNamespace}
* operations use the inverse transformation (that is, {@code remove} is
* the empty String {@code ""} and {@code add} is the {@link
* javax.management.namespace namespace} prefix).
* <br>
* On a more general scale, {@link #rewriteInput rewriteInput} removes
* {@link #newRewritingProcessor remove} and the prepend {@link
* #newRewritingProcessor add}, and {@link #rewriteOutput rewriteOutput}
* does the opposite, removing {@link #newRewritingProcessor add}, and
* then adding {@link #newRewritingProcessor remove}.
* <br>
* An implementation of {@code RewritingProcessor} should make sure that
* <code>rewriteInput(rewriteOutput(x,clp),clp)</code> and
* <code>rewriteOutput(rewriteInput(x,clp),clp)</code> always return
* {@code x} or an exact clone of {@code x}.
* </p>
* <p>A default implementation of {@code RewritingProcessor} based on
* Java Object Serialization can be
* obtained from {@link #newRewritingProcessor newRewritingProcessor}.
* </p>
* <p>
* By default, the instances of {@code RewritingProcessor} returned by
* {@link #newRewritingProcessor newRewritingProcessor} will rewrite
* ObjectNames contained in instances of classes they don't know about by
* serializing and then deserializing such object instances. This will
* happen even if such instances don't - or can't contain ObjectNames,
* because the default implementation of {@code RewritingProcessor} will
* not be able to determine whether instances of such classes can/do contain
* instance of ObjectNames before serializing/deserializing them.
* </p>
* <p>If you are using custom classes that the default implementation of
* {@code RewritingProcessor} don't know about, it can be interesting to
* prevent an instance of {@code RewritingProcessor} to serialize/deserialize
* instances of such classes for nothing. In that case, you could customize
* the behavior of such a {@code RewritingProcessor} by wrapping it in a
* custom subclass of {@code RewritingProcessor} as shown below:
* <pre>
* public class MyRewritingProcessor extends RewritingProcessor {
* MyRewritingProcessor(String remove, String add) {
* this(RewritingProcessor.newRewritingProcessor(remove,add));
* }
* MyRewritingProcessor(RewritingProcessor delegate) {
* super(delegate);
* }
*
* <T> T rewriteInput(T input) {
* if (input == null) return null;
* if (MyClass.equals(input.getClass())) {
* // I know that MyClass doesn't contain any ObjectName
* return (T) input;
* }
* return super.rewriteInput(input);
* }
* <T> T rewriteOutput(T result) {
* if (result == null) return null;
* if (MyClass.equals(result.getClass())) {
* // I know that MyClass doesn't contain any ObjectName
* return (T) result;
* }
* return super.rewriteOutput(result);
* }
* }
* </pre>
* </p>
* <p>Such a subclass may also provide an alternate way of rewriting
* custom subclasses for which rewriting is needed - for instance:
* <pre>
* public class MyRewritingProcessor extends RewritingProcessor {
* MyRewritingProcessor(String remove, String add) {
* this(RewritingProcessor.newRewritingProcessor(remove,add));
* }
* MyRewritingProcessor(RewritingProcessor delegate) {
* super(delegate);
* }
*
* <T> T rewriteInput(T input) {
* if (input == null) return null;
* if (MyClass.equals(input.getClass())) {
* // I know that MyClass doesn't contain any ObjectName
* return (T) input;
* } else if (MyOtherClass.equals(input.getClass())) {
* // Returns a new instance in which ObjectNames have been
* // replaced.
* final ObjectName aname = ((MyOtherClass)input).getName();
* return (T) (new MyOtherClass(super.rewriteInput(aname)));
* }
* return super.rewriteInput(input,clp);
* }
* <T> T rewriteOutput(T result) {
* if (result == null) return null;
* if (MyClass.equals(result.getClass())) {
* // I know that MyClass doesn't contain any ObjectName
* return (T) result;
* } else if (MyOtherClass.equals(result.getClass())) {
* // Returns a new instance in which ObjectNames have been
* // replaced.
* final ObjectName aname = ((MyOtherClass)result).getName();
* return (T) (new MyOtherClass(super.rewriteOutput(aname)));
* }
* return super.rewriteOutput(result,clp);
* }
* }
* </pre>
* </p>
* <p>If your application only uses {@link javax.management.MXBean MXBeans},
* or MBeans using simple types, and doesn't define any custom subclass of
* {@link javax.management.Notification}, you should never write such
* such {@code RewitingProcessor} implementations.
* </p>
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
public abstract class RewritingProcessor {
/**
* A logger for this class.
**/
private final RewritingProcessor delegate;
/**
* Creates a new instance of RewritingProcessor.
* <p>This is equivalent to calling {@link
* #RewritingProcessor(RewritingProcessor) RewritingProcessor(null)}.
* </p>
**/
protected RewritingProcessor() {
this(null);
}
/**
* Creates a new instance of RewritingProcessor, with a delegate.
* @param delegate a {@code RewritingProcessor} to which all the
* calls will be delegated. When implementing a subclass
* of {@code RewritingProcessor}, calling {@link
* #rewriteInput super.rewriteInput} will invoke
* {@code delegate.rewriteInput} and calling {@link
* #rewriteOutput super.rewriteOutput} will invoke
* {@code delegate.rewriteOutput}.
*
**/
protected RewritingProcessor(RewritingProcessor delegate) {
this.delegate = delegate;
}
/**
* Rewrites ObjectNames when {@link RewritingProcessor leaving} a {@link
* javax.management.namespace namespace}.
* <p>
* Returns {@code obj}, if it is known that {@code obj} doesn't contain
* any ObjectName, or a new copied instance of {@code obj} in which
* ObjectNames (if any) will have been rewritten, if {@code obj} contains
* ObjectNames, or if it is not known whether {@code obj} contains
* ObjectNames or not.
* </p>
* <p>
* The default implementation of this method is as follows: if the
* {@link #RewritingProcessor(RewritingProcessor) delegate} is {@code
* null}, throws an {@link IllegalArgumentException}. Otherwise,
* returns {@code delegate.rewriteOutput(obj)}.
* </p>
* <p>This behavior can be overridden by subclasses as shown in this
* class {@link RewritingProcessor description}.
* </p>
* @param obj The result to be rewritten if needed.
*
* @return {@code obj}, or a clone of {@code obj} in which ObjectNames
* have been rewritten. See this class {@link RewritingProcessor
* description} for more details.
* @throws IllegalArgumentException if this implementation does not know
* how to rewrite the object.
**/
public <T> T rewriteOutput(T obj) {
if (obj == null) return null;
if (delegate != null)
return delegate.rewriteOutput(obj);
throw new IllegalArgumentException("can't rewrite "+
obj.getClass().getName());
}
/**
* Rewrites ObjectNames when {@link RewritingProcessor entering} a {@link
* javax.management.namespace namespace}.
* <p>
* Returns {@code obj}, if it is known that {@code obj} doesn't contain
* any ObjectName, or a new copied instance of {@code obj} in which
* ObjectNames (if any) will have been rewritten, if {@code obj} contains
* ObjectNames, or if it is not known whether {@code obj} contains
* ObjectNames or not.
* </p>
* <p>
* The default implementation of this method is as follows: if the
* {@link #RewritingProcessor(RewritingProcessor) delegate} is {@code
* null}, throws an {@link IllegalArgumentException}. Otherwise,
* returns {@code delegate.rewriteInput(obj)}.
* </p>
* <p>This behavior can be overridden by subclasses as shown in this
* class {@link RewritingProcessor description}.
* </p>
* @param obj The result to be rewritten if needed.
* @return {@code obj}, or a clone of {@code obj} in which ObjectNames
* have been rewritten. See this class {@link RewritingProcessor
* description} for more details.
* @throws IllegalArgumentException if this implementation does not know
* how to rewrite the object.
**/
public <T> T rewriteInput(T obj) {
if (obj == null) return null;
if (delegate != null)
return delegate.rewriteInput(obj);
throw new IllegalArgumentException("can't rewrite "+
obj.getClass().getName());
}
/**
* Translate a routing ObjectName from the target (calling) context to
* the source (called) context when {@link RewritingProcessor entering} a
* {@link javax.management.namespace namespace}.
* <p>
* The default implementation of this method is as follows: if the
* {@link #RewritingProcessor(RewritingProcessor) delegate} is {@code
* null}, throws an {@link IllegalArgumentException}. Otherwise,
* returns {@code delegate.toSourceContext(targetName)}.
* </p>
* <p>This behavior can be overridden by subclasses as shown in this
* class {@link RewritingProcessor description}.
* </p>
* @param targetName The routing target ObjectName to translate.
* @return The ObjectName translated to the source context.
* @throws IllegalArgumentException if this implementation does not know
* how to rewrite the object.
**/
public ObjectName toSourceContext(ObjectName targetName) {
if (delegate != null)
return delegate.toSourceContext(targetName);
throw new IllegalArgumentException("can't rewrite targetName: "+
" no delegate.");
}
/**
* Translate an ObjectName returned from the source context into
* the target (calling) context when {@link RewritingProcessor leaving} a
* {@link javax.management.namespace namespace}.
* <p>
* The default implementation of this method is as follows: if the
* {@link #RewritingProcessor(RewritingProcessor) delegate} is {@code
* null}, throws an {@link IllegalArgumentException}. Otherwise,
* returns {@code delegate.toTargetContext(sourceName)}.
* </p>
* <p>This behavior can be overridden by subclasses as shown in this
* class {@link RewritingProcessor description}.
* </p>
* @param sourceName The routing source ObjectName to translate to the
* target context.
* @return The ObjectName translated to the target context.
* @throws IllegalArgumentException if this implementation does not know
* how to rewrite the object.
**/
public ObjectName toTargetContext(ObjectName sourceName) {
if (delegate != null)
return delegate.toTargetContext(sourceName);
throw new IllegalArgumentException("can't rewrite sourceName: "+
" no delegate.");
}
/**
* Translate an ObjectInstance returned from the source context into
* the target (calling) context when {@link RewritingProcessor leaving} a
* {@link javax.management.namespace namespace}.
* <p>
* The default implementation of this method is as follows: if the
* {@link #RewritingProcessor(RewritingProcessor) delegate} is {@code
* null}, throws an {@link IllegalArgumentException}. Otherwise,
* returns {@code delegate.toTargetContext(sourceMoi)}.
* </p>
* <p>This behavior can be overridden by subclasses as shown in this
* class {@link RewritingProcessor description}.
* </p>
* @param sourceMoi The routing source ObjectInstance to translate.
* @return The ObjectInstance translated to the target context.
* @throws IllegalArgumentException if this implementation does not know
* how to rewrite the object.
**/
public ObjectInstance toTargetContext(ObjectInstance sourceMoi) {
if (delegate != null)
return delegate.toTargetContext(sourceMoi);
throw new IllegalArgumentException("can't rewrite sourceName: "+
" no delegate.");
}
/**
* Creates a new default instance of {@link RewritingProcessor}.
* @param remove The prefix to remove from {@link ObjectName ObjectNames}
* when {@link RewritingProcessor entering} the {@link
* javax.management.namespace namespace}.
* @param add The prefix to add to {@link ObjectName ObjectNames}
* when {@link RewritingProcessor entering} the {@link
* javax.management.namespace namespace} (this is performed
* after having removed the {@code remove} prefix.
* @return A new {@link RewritingProcessor} processor object that will
* perform the requested operation, using Java serialization if
* necessary.
**/
public static RewritingProcessor newRewritingProcessor(String remove,
String add) {
return new DefaultRewritingProcessor(remove,add);
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace.serial;
import com.sun.jmx.namespace.ObjectNameRouter;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
/**
* Class RoutingOnlyProcessor. A RewritingProcessor that uses
* Java Serialization to rewrite ObjectNames contained in
* input and results...
*
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
class RoutingOnlyProcessor extends RewritingProcessor {
final ObjectNameRouter router;
public RoutingOnlyProcessor(String targetDirName) {
this(targetDirName,null);
}
/** Creates a new instance of RoutingOnlyProcessor */
public RoutingOnlyProcessor(final String remove, final String add) {
super(new IdentityProcessor());
if (remove == null || add == null)
throw new IllegalArgumentException("Null argument");
router = new ObjectNameRouter(remove,add);
}
@Override
public final ObjectName toTargetContext(ObjectName sourceName) {
return router.toTargetContext(sourceName,false);
}
@Override
public final ObjectName toSourceContext(ObjectName targetName) {
return router.toSourceContext(targetName,false);
}
@Override
public final ObjectInstance toTargetContext(ObjectInstance sourceMoi) {
return router.toTargetContext(sourceMoi,false);
}
}

View file

@ -0,0 +1,172 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.namespace.serial;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.Queue;
import javax.management.ObjectName;
/**
* Class SerialRewritingProcessor. A RewritingProcessor that uses
* Java Serialization to rewrite ObjectNames contained in
* input & results...
* <p><b>
* This API is a Sun internal API and is subject to changes without notice.
* </b></p>
* @since 1.7
*/
class SerialRewritingProcessor extends RewritingProcessor {
private static class CloneOutput extends ObjectOutputStream {
Queue<Class<?>> classQueue = new LinkedList<Class<?>>();
CloneOutput(OutputStream out) throws IOException {
super(out);
}
@Override
protected void annotateClass(Class<?> c) {
classQueue.add(c);
}
@Override
protected void annotateProxyClass(Class<?> c) {
classQueue.add(c);
}
}
private static class CloneInput extends ObjectInputStream {
private final CloneOutput output;
CloneInput(InputStream in, CloneOutput output) throws IOException {
super(in);
this.output = output;
}
@Override
protected Class<?> resolveClass(ObjectStreamClass osc)
throws IOException, ClassNotFoundException {
Class<?> c = output.classQueue.poll();
String expected = osc.getName();
String found = (c == null) ? null : c.getName();
if (!expected.equals(found)) {
throw new InvalidClassException("Classes desynchronized: " +
"found " + found + " when expecting " + expected);
}
return c;
}
@Override
protected Class<?> resolveProxyClass(String[] interfaceNames)
throws IOException, ClassNotFoundException {
return output.classQueue.poll();
}
}
final String targetPrefix;
final String sourcePrefix;
final boolean identity;
public SerialRewritingProcessor(String targetDirName) {
this(targetDirName,null);
}
/** Creates a new instance of SerialRewritingProcessor */
public SerialRewritingProcessor(final String remove, final String add) {
super(new RoutingOnlyProcessor(remove,add));
this.targetPrefix = remove;
this.sourcePrefix = add;
identity = targetPrefix.equals(sourcePrefix);
}
private <T> T switchContext(T result, String from,String to)
throws IOException, ClassNotFoundException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final CloneOutput ostream = new CloneOutput(baos);
JMXNamespaceContext.serialize(ostream,result,from,null);
ostream.flush();
final byte[] bytes = baos.toByteArray();
final ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
final CloneInput istream = new CloneInput(bais, ostream);
@SuppressWarnings("unchecked")
final T clone = (T) JMXNamespaceContext.deserialize(istream,null,to);
return clone;
}
@Override
@SuppressWarnings("unchecked")
public <T> T rewriteOutput(T result) {
if (identity) return result;
return (T) processOutput(result);
}
private Object processOutput(Object result) {
try {
if (result instanceof ObjectName)
return toTargetContext((ObjectName) result);
return switchContext(result,sourcePrefix,targetPrefix);
} catch (ClassNotFoundException x) {
throw new IllegalArgumentException("Can't process result: "+x,x);
} catch (IOException x) {
throw new IllegalArgumentException("Can't process result: "+x,x);
}
}
@Override
@SuppressWarnings("unchecked")
public <T> T rewriteInput(T input) {
if (identity) return input;
return (T) processInput(input);
}
private Object processInput(Object input) {
try {
if (input instanceof ObjectName)
return toSourceContext((ObjectName) input);
return switchContext(input,targetPrefix,sourcePrefix);
} catch (ClassNotFoundException x) {
throw new IllegalArgumentException("Can't process input: "+x,x);
} catch (IOException x) {
throw new IllegalArgumentException("Can't process input: "+x,x);
}
}
}

View file

@ -0,0 +1,44 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>The <code>com.sun.jmx.namespace.serial</code> package</title>
<!--
Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 only, as
published by the Free Software Foundation. Sun designates this
particular file as subject to the "Classpath" exception as provided
by Sun in the LICENSE file that accompanied this code.
This code 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
version 2 for more details (a copy is included in the LICENSE file that
accompanied this code).
You should have received a copy of the GNU General Public License version
2 along with this work; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
CA 95054 USA or visit www.sun.com if you need additional information or
have any questions.
-->
</head>
<body bgcolor="white">
<p>The <code>com.sun.jmx.namespace.serial</code> package contains
sun specific implementation classes used to switch namespace
prefixes in ObjectName during serialization.
</p>
<p><b>NEVER USE THESE CLASSES DIRECTLY</b></p>
<p><b>
This API is a Sun internal API and is subject to changes without notice.
</b></p>
<p>The public API through which these proprietary classes can be invoked is
located in <code>javax.management.namespace.JMXNamespaces</code>
</p>
</body>
</html>

View file

@ -57,6 +57,7 @@ import javax.security.auth.Subject;
public class ServerNotifForwarder {
public ServerNotifForwarder(MBeanServer mbeanServer,
Map env,
NotificationBuffer notifBuffer,
@ -85,7 +86,8 @@ public class ServerNotifForwarder {
// Explicitly check MBeanPermission for addNotificationListener
//
checkMBeanPermission(name, "addNotificationListener");
checkMBeanPermission(getMBeanServerName(),
mbeanServer, name, "addNotificationListener");
if (notificationAccessController != null) {
notificationAccessController.addNotificationListener(
connectionId, name, getSubject());
@ -155,7 +157,8 @@ public class ServerNotifForwarder {
// Explicitly check MBeanPermission for removeNotificationListener
//
checkMBeanPermission(name, "removeNotificationListener");
checkMBeanPermission(getMBeanServerName(),
mbeanServer, name, "removeNotificationListener");
if (notificationAccessController != null) {
notificationAccessController.removeNotificationListener(
connectionId, name, getSubject());
@ -330,13 +333,7 @@ public class ServerNotifForwarder {
* Explicitly check the MBeanPermission for
* the current access control context.
*/
private void checkMBeanPermission(final ObjectName name,
final String actions)
throws InstanceNotFoundException, SecurityException {
checkMBeanPermission(mbeanServer, name, actions);
}
public static void checkMBeanPermission(
public static void checkMBeanPermission(String serverName,
final MBeanServer mbs, final ObjectName name, final String actions)
throws InstanceNotFoundException, SecurityException {
SecurityManager sm = System.getSecurityManager();
@ -355,7 +352,9 @@ public class ServerNotifForwarder {
throw (InstanceNotFoundException) extractException(e);
}
String classname = oi.getClassName();
MBeanPermission perm = new MBeanPermission(classname,
MBeanPermission perm = new MBeanPermission(
serverName,
classname,
null,
name,
actions);
@ -370,8 +369,8 @@ public class ServerNotifForwarder {
TargetedNotification tn) {
try {
if (checkNotificationEmission) {
checkMBeanPermission(
name, "addNotificationListener");
checkMBeanPermission(getMBeanServerName(),
mbeanServer, name, "addNotificationListener");
}
if (notificationAccessController != null) {
notificationAccessController.fetchNotification(
@ -433,11 +432,27 @@ public class ServerNotifForwarder {
}
}
private String getMBeanServerName() {
if (mbeanServerName != null) return mbeanServerName;
else return (mbeanServerName = getMBeanServerName(mbeanServer));
}
private static String getMBeanServerName(final MBeanServer server) {
final PrivilegedAction<String> action = new PrivilegedAction<String>() {
public String run() {
return Util.getMBeanServerSecurityName(server);
}
};
return AccessController.doPrivileged(action);
}
//------------------
// PRIVATE VARIABLES
//------------------
private MBeanServer mbeanServer;
private volatile String mbeanServerName;
private final String connectionId;

View file

@ -25,6 +25,7 @@
package com.sun.jmx.remote.util;
import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.event.EventClientFactory;
import java.lang.reflect.InvocationHandler;
@ -45,6 +46,7 @@ import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.event.EventClient;
import javax.management.event.EventClientDelegate;
import javax.management.namespace.JMXNamespaces;
/**
* Class EventClientConnection - a {@link Proxy} that wraps an
@ -63,12 +65,10 @@ public class EventClientConnection implements InvocationHandler,
/**
* A logger for this class.
**/
private static final Logger LOG =
Logger.getLogger(EventClientConnection.class.getName());
private static final Logger LOG = JmxProperties.NOTIFICATION_LOGGER;
private static final String NAMESPACE_SEPARATOR = "//";
private static final int NAMESPACE_SEPARATOR_LENGTH =
NAMESPACE_SEPARATOR.length();
JMXNamespaces.NAMESPACE_SEPARATOR.length();
/**
* Creates a new {@code EventClientConnection}.
@ -212,9 +212,9 @@ public class EventClientConnection implements InvocationHandler,
}
final ObjectName mbean = (ObjectName) args[0];
final EventClient client = getEventClient();
final EventClient evtClient = getEventClient();
// Fails if client is null AND the MBean we try to listen to is
// Fails if evtClient is null AND the MBean we try to listen to is
// in a subnamespace. We fail here because we know this will not
// work.
//
@ -222,15 +222,15 @@ public class EventClientConnection implements InvocationHandler,
// earlier agent (JDK 1.6 or earlier), then the EventClient will
// be null (we can't use the event service with earlier JDKs).
//
// In principle a null client indicates that the remote VM is of
// In principle a null evtClient indicates that the remote VM is of
// an earlier version, in which case it shouldn't contain any namespace.
//
// So having a null client AND an MBean contained in a namespace is
// So having a null evtClient AND an MBean contained in a namespace is
// clearly an error case.
//
if (client == null) {
if (evtClient == null) {
final String domain = mbean.getDomain();
final int index = domain.indexOf(NAMESPACE_SEPARATOR);
final int index = domain.indexOf(JMXNamespaces.NAMESPACE_SEPARATOR);
if (index > -1 && index <
(domain.length()-NAMESPACE_SEPARATOR_LENGTH)) {
throw new UnsupportedOperationException(method.getName()+
@ -256,9 +256,9 @@ public class EventClientConnection implements InvocationHandler,
final NotificationFilter filter = (NotificationFilter) args[2];
final Object handback = args[3];
if (client != null) {
if (evtClient != null) {
// general case
client.addNotificationListener(mbean,listener,filter,handback);
evtClient.addNotificationListener(mbean,listener,filter,handback);
} else {
// deprecated case. Only works for mbean in local namespace.
connection.addNotificationListener(mbean,listener,filter,
@ -274,9 +274,9 @@ public class EventClientConnection implements InvocationHandler,
switch (nargs) {
case 2:
if (client != null) {
if (evtClient != null) {
// general case
client.removeNotificationListener(mbean,listener);
evtClient.removeNotificationListener(mbean,listener);
} else {
// deprecated case. Only works for mbean in local namespace.
connection.removeNotificationListener(mbean, listener);
@ -286,8 +286,8 @@ public class EventClientConnection implements InvocationHandler,
case 4:
NotificationFilter filter = (NotificationFilter) args[2];
Object handback = args[3];
if (client != null) {
client.removeNotificationListener(mbean,
if (evtClient != null) {
evtClient.removeNotificationListener(mbean,
listener,
filter,
handback);

View file

@ -25,7 +25,6 @@
package java.net;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import sun.security.action.*;

View file

@ -0,0 +1,39 @@
/*
* Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package java.net;
/**
* Represents a family of communication protocols.
*
* @since 1.7
*/
public interface ProtocolFamily {
/**
* Returns the name of the protocol family.
*/
String name();
}

View file

@ -0,0 +1,55 @@
/*
* Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package java.net;
/**
* A socket option associated with a socket.
*
* <p> In the {@link java.nio.channels channels} package, the {@link
* java.nio.channels.NetworkChannel} interface defines the {@link
* java.nio.channels.NetworkChannel#setOption(SocketOption,Object) setOption}
* and {@link java.nio.channels.NetworkChannel#getOption(SocketOption) getOption}
* methods to set and query the channel's socket options.
*
* @param <T> The type of the socket option value.
*
* @since 1.7
*
* @see StandardSocketOption
*/
public interface SocketOption<T> {
/**
* Returns the name of the socket option.
*/
String name();
/**
* Returns the type of the socket option value.
*/
Class<T> type();
}

View file

@ -0,0 +1,45 @@
/*
* Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package java.net;
/**
* Defines the standard family of communication protocols.
*
* @since 1.7
*/
public enum StandardProtocolFamily implements ProtocolFamily {
/**
* Internet Protocol Version 4 (IPv4)
*/
INET,
/**
* Internet Protocol Version 6 (IPv6)
*/
INET6
}

View file

@ -0,0 +1,352 @@
/*
* Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package java.net;
/**
* Defines the <em>standard</em> socket options.
*
* <p> The {@link SocketOption#name name} of each socket option defined by this
* class is its field name.
*
* <p> In this release, the socket options defined here are used by {@link
* java.nio.channels.NetworkChannel network} channels in the {@link
* java.nio.channels channels} package.
*
* @since 1.7
*/
public final class StandardSocketOption {
private StandardSocketOption() { }
// -- SOL_SOCKET --
/**
* Allow transmission of broadcast datagrams.
*
* <p> The value of this socket option is a {@code Boolean} that represents
* whether the option is enabled or disabled. The option is specific to
* datagram-oriented sockets sending to {@link java.net.Inet4Address IPv4}
* broadcast addresses. When the socket option is enabled then the socket
* can be used to send <em>broadcast datagrams</em>.
*
* <p> The initial value of this socket option is {@code FALSE}. The socket
* option may be enabled or disabled at any time. Some operating systems may
* require that the Java virtual machine be started with implementation
* specific privileges to enable this option or send broadcast datagrams.
*
* @see <a href="http://www.ietf.org/rfc/rfc919.txt">RFC&nbsp;929:
* Broadcasting Internet Datagrams</a>
*/
public static final SocketOption<Boolean> SO_BROADCAST =
new StdSocketOption<Boolean>("SO_BROADCAST", Boolean.class);
/**
* Keep connection alive.
*
* <p> The value of this socket option is a {@code Boolean} that represents
* whether the option is enabled or disabled. When the {@code SO_KEEPALIVE}
* option is enabled the operating system may use a <em>keep-alive</em>
* mechanism to periodically probe the other end of a connection when the
* connection is otherwise idle. The exact semantics of the keep alive
* mechanism is system dependent and therefore unspecified.
*
* <p> The initial value of this socket option is {@code FALSE}. The socket
* option may be enabled or disabled at any time.
*
* @see <a href="http://www.ietf.org/rfc/rfc1122.txt">RFC&nbsp;1122
* Requirements for Internet Hosts -- Communication Layers</a>
*/
public static final SocketOption<Boolean> SO_KEEPALIVE =
new StdSocketOption<Boolean>("SO_KEEPALIVE", Boolean.class);
/**
* The size of the socket send buffer.
*
* <p> The value of this socket option is an {@code Integer} that is the
* size of the socket send buffer in bytes. The socket send buffer is an
* output buffer used by the networking implementation. It may need to be
* increased for high-volume connections. The value of the socket option is
* a <em>hint</em> to the implementation to size the buffer and the actual
* size may differ. The socket option can be queried to retrieve the actual
* size.
*
* <p> For datagram-oriented sockets, the size of the send buffer may limit
* the size of the datagrams that may be sent by the socket. Whether
* datagrams larger than the buffer size are sent or discarded is system
* dependent.
*
* <p> The initial/default size of the socket send buffer and the range of
* allowable values is system dependent although a negative size is not
* allowed. An attempt to set the socket send buffer to larger than its
* maximum size causes it to be set to its maximum size.
*
* <p> An implementation allows this socket option to be set before the
* socket is bound or connected. Whether an implementation allows the
* socket send buffer to be changed after the socket is bound is system
* dependent.
*/
public static final SocketOption<Integer> SO_SNDBUF =
new StdSocketOption<Integer>("SO_SNDBUF", Integer.class);
/**
* The size of the socket receive buffer.
*
* <p> The value of this socket option is an {@code Integer} that is the
* size of the socket receive buffer in bytes. The socket receive buffer is
* an input buffer used by the networking implementation. It may need to be
* increased for high-volume connections or decreased to limit the possible
* backlog of incoming data. The value of the socket option is a
* <em>hint</em> to the implementation to size the buffer and the actual
* size may differ.
*
* <p> For datagram-oriented sockets, the size of the receive buffer may
* limit the size of the datagrams that can be received. Whether datagrams
* larger than the buffer size can be received is system dependent.
* Increasing the socket receive buffer may be important for cases where
* datagrams arrive in bursts faster than they can be processed.
*
* <p> In the case of stream-oriented sockets and the TCP/IP protocol, the
* size of the socket receive buffer may be used when advertising the size
* of the TCP receive window to the remote peer.
*
* <p> The initial/default size of the socket receive buffer and the range
* of allowable values is system dependent although a negative size is not
* allowed. An attempt to set the socket receive buffer to larger than its
* maximum size causes it to be set to its maximum size.
*
* <p> An implementation allows this socket option to be set before the
* socket is bound or connected. Whether an implementation allows the
* socket receive buffer to be changed after the socket is bound is system
* dependent.
*
* @see <a href="http://www.ietf.org/rfc/rfc1323.txt">RFC&nbsp;1323: TCP
* Extensions for High Performance</a>
*/
public static final SocketOption<Integer> SO_RCVBUF =
new StdSocketOption<Integer>("SO_RCVBUF", Integer.class);
/**
* Re-use address.
*
* <p> The value of this socket option is a {@code Boolean} that represents
* whether the option is enabled or disabled. The exact semantics of this
* socket option are socket type and system dependent.
*
* <p> In the case of stream-oriented sockets, this socket option will
* usually determine whether the socket can be bound to a socket address
* when a previous connection involving that socket address is in the
* <em>TIME_WAIT</em> state. On implementations where the semantics differ,
* and the socket option is not required to be enabled in order to bind the
* socket when a previous connection is in this state, then the
* implementation may choose to ignore this option.
*
* <p> For datagram-oriented sockets the socket option is used to allow
* multiple programs bind to the same address. This option should be enabled
* when the socket is to be used for Internet Protocol (IP) multicasting.
*
* <p> An implementation allows this socket option to be set before the
* socket is bound or connected. Changing the value of this socket option
* after the socket is bound has no effect. The default value of this
* socket option is system dependent.
*
* @see <a href="http://www.ietf.org/rfc/rfc793.txt">RFC&nbsp;793: Transmission
* Control Protocol</a>
*/
public static final SocketOption<Boolean> SO_REUSEADDR =
new StdSocketOption<Boolean>("SO_REUSEADDR", Boolean.class);
/**
* Linger on close if data is present.
*
* <p> The value of this socket option is an {@code Integer} that controls
* the action taken when unsent data is queued on the socket and a method
* to close the socket is invoked. If the value of the socket option is zero
* or greater, then it represents a timeout value, in seconds, known as the
* <em>linger interval</em>. The linger interval is the timeout for the
* {@code close} method to block while the operating system attempts to
* transmit the unsent data or it decides that it is unable to transmit the
* data. If the value of the socket option is less than zero then the option
* is disabled. In that case the {@code close} method does not wait until
* unsent data is transmitted; if possible the operating system will transmit
* any unsent data before the connection is closed.
*
* <p> This socket option is intended for use with sockets that are configured
* in {@link java.nio.channels.SelectableChannel#isBlocking() blocking} mode
* only. The behavior of the {@code close} method when this option is
* enabled on a non-blocking socket is not defined.
*
* <p> The initial value of this socket option is a negative value, meaning
* that the option is disabled. The option may be enabled, or the linger
* interval changed, at any time. The maximum value of the linger interval
* is system dependent. Setting the linger interval to a value that is
* greater than its maximum value causes the linger interval to be set to
* its maximum value.
*/
public static final SocketOption<Integer> SO_LINGER =
new StdSocketOption<Integer>("SO_LINGER", Integer.class);
// -- IPPROTO_IP --
/**
* The Type of Service (ToS) octet in the Internet Protocol (IP) header.
*
* <p> The value of this socket option is an {@code Integer}, the least
* significant 8 bits of which represents the value of the ToS octet in IP
* packets sent by sockets to an {@link StandardProtocolFamily#INET IPv4}
* socket. The interpretation of the ToS octet is network specific and
* is not defined by this class. Further information on the ToS octet can be
* found in <a href="http://www.ietf.org/rfc/rfc1349.txt">RFC&nbsp;1349</a>
* and <a href="http://www.ietf.org/rfc/rfc2474.txt">RFC&nbsp;2474</a>. The
* value of the socket option is a <em>hint</em>. An implementation may
* ignore the value, or ignore specific values.
*
* <p> The initial/default value of the TOS field in the ToS octet is
* implementation specific but will typically be {@code 0}. For
* datagram-oriented sockets the option may be configured at any time after
* the socket has been bound. The new value of the octet is used when sending
* subsequent datagrams. It is system dependent whether this option can be
* queried or changed prior to binding the socket.
*
* <p> The behavior of this socket option on a stream-oriented socket, or an
* {@link StandardProtocolFamily#INET6 IPv6} socket, is not defined in this
* release.
*/
public static final SocketOption<Integer> IP_TOS =
new StdSocketOption<Integer>("IP_TOS", Integer.class);
/**
* The network interface for Internet Protocol (IP) multicast datagrams.
*
* <p> The value of this socket option is a {@link NetworkInterface} that
* represents the outgoing interface for multicast datagrams sent by the
* datagram-oriented socket. For {@link StandardProtocolFamily#INET6 IPv6}
* sockets then it is system dependent whether setting this option also
* sets the outgoing interface for multlicast datagrams sent to IPv4
* addresses.
*
* <p> The initial/default value of this socket option may be {@code null}
* to indicate that outgoing interface will be selected by the operating
* system, typically based on the network routing tables. An implementation
* allows this socket option to be set after the socket is bound. Whether
* the socket option can be queried or changed prior to binding the socket
* is system dependent.
*
* @see java.nio.channels.MulticastChannel
*/
public static final SocketOption<NetworkInterface> IP_MULTICAST_IF =
new StdSocketOption<NetworkInterface>("IP_MULTICAST_IF", NetworkInterface.class);
/**
* The <em>time-to-live</em> for Internet Protocol (IP) multicast datagrams.
*
* <p> The value of this socket option is an {@code Integer} in the range
* <tt>0&nbsp;<=&nbsp;value&nbsp;<=&nbsp;255</tt>. It is used to control
* the scope of multicast datagrams sent by the datagram-oriented socket.
* In the case of an {@link StandardProtocolFamily#INET IPv4} socket
* the option is the time-to-live (TTL) on multicast datagrams sent by the
* socket. Datagrams with a TTL of zero are not transmitted on the network
* but may be delivered locally. In the case of an {@link
* StandardProtocolFamily#INET6 IPv6} socket the option is the
* <em>hop limit</em> which is number of <em>hops</em> that the datagram can
* pass through before expiring on the network. For IPv6 sockets it is
* system dependent whether the option also sets the <em>time-to-live</em>
* on multicast datagrams sent to IPv4 addresses.
*
* <p> The initial/default value of the time-to-live setting is typically
* {@code 1}. An implementation allows this socket option to be set after
* the socket is bound. Whether the socket option can be queried or changed
* prior to binding the socket is system dependent.
*
* @see java.nio.channels.MulticastChannel
*/
public static final SocketOption<Integer> IP_MULTICAST_TTL =
new StdSocketOption<Integer>("IP_MULTICAST_TTL", Integer.class);
/**
* Loopback for Internet Protocol (IP) multicast datagrams.
*
* <p> The value of this socket option is a {@code Boolean} that controls
* the <em>loopback</em> of multicast datagrams. The value of the socket
* option represents if the option is enabled or disabled.
*
* <p> The exact semantics of this socket options are system dependent.
* In particular, it is system dependent whether the loopback applies to
* multicast datagrams sent from the socket or received by the socket.
* For {@link StandardProtocolFamily#INET6 IPv6} sockets then it is
* system dependent whether the option also applies to multicast datagrams
* sent to IPv4 addresses.
*
* <p> The initial/default value of this socket option is {@code TRUE}. An
* implementation allows this socket option to be set after the socket is
* bound. Whether the socket option can be queried or changed prior to
* binding the socket is system dependent.
*
* @see java.nio.channels.MulticastChannel
*/
public static final SocketOption<Boolean> IP_MULTICAST_LOOP =
new StdSocketOption<Boolean>("IP_MULTICAST_LOOP", Boolean.class);
// -- IPPROTO_TCP --
/**
* Disable the Nagle algorithm.
*
* <p> The value of this socket option is a {@code Boolean} that represents
* whether the option is enabled or disabled. The socket option is specific to
* stream-oriented sockets using the TCP/IP protocol. TCP/IP uses an algorithm
* known as <em>The Nagle Algorithm</em> to coalesce short segments and
* improve network efficiency.
*
* <p> The default value of this socket option is {@code FALSE}. The
* socket option should only be enabled in cases where it is known that the
* coalescing impacts performance. The socket option may be enabled at any
* time. In other words, the Nagle Algorithm can be disabled. Once the option
* is enabled, it is system dependent whether it can be subsequently
* disabled. In that case, invoking the {@code setOption} method to disable
* the option has no effect.
*
* @see <a href="http://www.ietf.org/rfc/rfc1122.txt">RFC&nbsp;1122:
* Requirements for Internet Hosts -- Communication Layers</a>
*/
public static final SocketOption<Boolean> TCP_NODELAY =
new StdSocketOption<Boolean>("TCP_NODELAY", Boolean.class);
private static class StdSocketOption<T> implements SocketOption<T> {
private final String name;
private final Class<T> type;
StdSocketOption(String name, Class<T> type) {
this.name = name;
this.type = type;
}
@Override public String name() { return name; }
@Override public Class<T> type() { return type; }
@Override public String toString() { return name; }
}
}

View file

@ -735,14 +735,68 @@ class Bits { // package-private
static final int JNI_COPY_TO_ARRAY_THRESHOLD = 6;
static final int JNI_COPY_FROM_ARRAY_THRESHOLD = 6;
// This number limits the number of bytes to copy per call to Unsafe's
// copyMemory method. A limit is imposed to allow for safepoint polling
// during a large copy
static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L;
// These methods do no bounds checking. Verification that the copy will not
// result in memory corruption should be done prior to invocation.
// All positions and lengths are specified in bytes.
static native void copyFromByteArray(Object src, long srcPos, long dstAddr,
long length);
static native void copyToByteArray(long srcAddr, Object dst, long dstPos,
long length);
/**
* Copy from given source array to destination address.
*
* @param src
* source array
* @param srcBaseOffset
* offset of first element of storage in source array
* @param srcPos
* offset within source array of the first element to read
* @param dstAddr
* destination address
* @param length
* number of bytes to copy
*/
static void copyFromArray(Object src, long srcBaseOffset, long srcPos,
long dstAddr, long length)
{
long offset = srcBaseOffset + srcPos;
while (length > 0) {
long size = (length > UNSAFE_COPY_THRESHOLD) ? UNSAFE_COPY_THRESHOLD : length;
unsafe.copyMemory(src, offset, null, dstAddr, size);
length -= size;
offset += size;
dstAddr += size;
}
}
/**
* Copy from source address into given destination array.
*
* @param srcAddr
* source address
* @param dst
* destination array
* @param dstBaseOffset
* offset of first element of storage in destination array
* @param dstPos
* offset within destination array of the first element to write
* @param length
* number of bytes to copy
*/
static void copyToArray(long srcAddr, Object dst, long dstBaseOffset, long dstPos,
long length)
{
long offset = dstBaseOffset + dstPos;
while (length > 0) {
long size = (length > UNSAFE_COPY_THRESHOLD) ? UNSAFE_COPY_THRESHOLD : length;
unsafe.copyMemory(null, srcAddr, dst, offset, size);
length -= size;
srcAddr += size;
offset += size;
}
}
static void copyFromCharArray(Object src, long srcPos, long dstAddr,
long length)

View file

@ -47,6 +47,9 @@ class Direct$Type$Buffer$RW$$BO$
// Cached unsafe-access object
protected static final Unsafe unsafe = Bits.unsafe();
// Cached array base offset
private static final long arrayBaseOffset = (long)unsafe.arrayBaseOffset($type$[].class);
// Cached unaligned-access capability
protected static final boolean unaligned = Bits.unaligned();
@ -242,12 +245,14 @@ class Direct$Type$Buffer$RW$$BO$
if (length > rem)
throw new BufferUnderflowException();
#if[!byte]
if (order() != ByteOrder.nativeOrder())
Bits.copyTo$Memtype$Array(ix(pos), dst,
offset << $LG_BYTES_PER_VALUE$,
length << $LG_BYTES_PER_VALUE$);
else
Bits.copyToByteArray(ix(pos), dst,
#end[!byte]
Bits.copyToArray(ix(pos), dst, arrayBaseOffset,
offset << $LG_BYTES_PER_VALUE$,
length << $LG_BYTES_PER_VALUE$);
position(pos + length);
@ -332,11 +337,13 @@ class Direct$Type$Buffer$RW$$BO$
if (length > rem)
throw new BufferOverflowException();
#if[!byte]
if (order() != ByteOrder.nativeOrder())
Bits.copyFrom$Memtype$Array(src, offset << $LG_BYTES_PER_VALUE$,
ix(pos), length << $LG_BYTES_PER_VALUE$);
else
Bits.copyFromByteArray(src, offset << $LG_BYTES_PER_VALUE$,
#end[!byte]
Bits.copyFromArray(src, arrayBaseOffset, offset << $LG_BYTES_PER_VALUE$,
ix(pos), length << $LG_BYTES_PER_VALUE$);
position(pos + length);
} else {

View file

@ -26,28 +26,21 @@
package java.nio.channels;
import java.io.IOException;
import java.net.ProtocolFamily;
import java.net.DatagramSocket;
import java.net.SocketOption;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.spi.*;
/**
* A selectable channel for datagram-oriented sockets.
*
*
* <p> Datagram channels are not a complete abstraction of network datagram
* sockets. Binding and the manipulation of socket options must be done
* through an associated {@link java.net.DatagramSocket} object obtained by
* invoking the {@link #socket() socket} method. It is not possible to create
* a channel for an arbitrary, pre-existing datagram socket, nor is it possible
* to specify the {@link java.net.DatagramSocketImpl} object to be used by a
* datagram socket associated with a datagram channel.
*
* <p> A datagram channel is created by invoking the {@link #open open} method
* of this class. A newly-created datagram channel is open but not connected.
* A datagram channel need not be connected in order for the {@link #send send}
* and {@link #receive receive} methods to be used. A datagram channel may be
* <p> A datagram channel is created by invoking one of the {@link #open open} methods
* of this class. It is not possible to create a channel for an arbitrary,
* pre-existing datagram socket. A newly-created datagram channel is open but not
* connected. A datagram channel need not be connected in order for the {@link #send
* send} and {@link #receive receive} methods to be used. A datagram channel may be
* connected, by invoking its {@link #connect connect} method, in order to
* avoid the overhead of the security checks are otherwise performed as part of
* every send and receive operation. A datagram channel must be connected in
@ -59,11 +52,57 @@ import java.nio.channels.spi.*;
* disconnected or closed. Whether or not a datagram channel is connected may
* be determined by invoking its {@link #isConnected isConnected} method.
*
* <p> Socket options are configured using the {@link #setOption(SocketOption,Object)
* setOption} method. Datagram channels support the following options:
* <blockquote>
* <table border>
* <tr>
* <th>Option Name</th>
* <th>Description</th>
* </tr>
* <tr>
* <td> {@link java.net.StandardSocketOption#SO_SNDBUF SO_SNDBUF} </td>
* <td> The size of the socket send buffer </td>
* </tr>
* <tr>
* <td> {@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} </td>
* <td> The size of the socket receive buffer </td>
* </tr>
* <tr>
* <td> {@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} </td>
* <td> Re-use address </td>
* </tr>
* <tr>
* <td> {@link java.net.StandardSocketOption#SO_BROADCAST SO_BROADCAST} </td>
* <td> Allow transmission of broadcast datagrams </td>
* </tr>
* <tr>
* <td> {@link java.net.StandardSocketOption#IP_TOS IP_TOS} </td>
* <td> The Type of Service (ToS) octet in the Internet Protocol (IP) header </td>
* </tr>
* <tr>
* <td> {@link java.net.StandardSocketOption#IP_MULTICAST_IF IP_MULTICAST_IF} </td>
* <td> The network interface for Internet Protocol (IP) multicast datagrams </td>
* </tr>
* <tr>
* <td> {@link java.net.StandardSocketOption#IP_MULTICAST_TTL
* IP_MULTICAST_TTL} </td>
* <td> The <em>time-to-live</em> for Internet Protocol (IP) multicast
* datagrams </td>
* </tr>
* <tr>
* <td> {@link java.net.StandardSocketOption#IP_MULTICAST_LOOP
* IP_MULTICAST_LOOP} </td>
* <td> Loopback for Internet Protocol (IP) multicast datagrams </td>
* </tr>
* </table>
* </blockquote>
* Additional (implementation specific) options may also be supported.
*
* <p> Datagram channels are safe for use by multiple concurrent threads. They
* support concurrent reading and writing, though at most one thread may be
* reading and at most one thread may be writing at any given time. </p>
*
*
* @author Mark Reinhold
* @author JSR-51 Expert Group
* @since 1.4
@ -71,7 +110,7 @@ import java.nio.channels.spi.*;
public abstract class DatagramChannel
extends AbstractSelectableChannel
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, MulticastChannel
{
/**
@ -88,7 +127,13 @@ public abstract class DatagramChannel
* java.nio.channels.spi.SelectorProvider#openDatagramChannel()
* openDatagramChannel} method of the system-wide default {@link
* java.nio.channels.spi.SelectorProvider} object. The channel will not be
* connected. </p>
* connected.
*
* <p> The {@link ProtocolFamily ProtocolFamily} of the channel's socket
* is platform (and possibly configuration) dependent and therefore unspecified.
* The {@link #open(ProtocolFamily) open} allows the protocol family to be
* selected when opening a datagram channel, and should be used to open
* datagram channels that are intended for Internet Protocol multicasting.
*
* @return A new datagram channel
*
@ -99,6 +144,39 @@ public abstract class DatagramChannel
return SelectorProvider.provider().openDatagramChannel();
}
/**
* Opens a datagram channel.
*
* <p> The {@code family} parameter is used to specify the {@link
* ProtocolFamily}. If the datagram channel is to be used for IP multicasing
* then this should correspond to the address type of the multicast groups
* that this channel will join.
*
* <p> The new channel is created by invoking the {@link
* java.nio.channels.spi.SelectorProvider#openDatagramChannel(ProtocolFamily)
* openDatagramChannel} method of the system-wide default {@link
* java.nio.channels.spi.SelectorProvider} object. The channel will not be
* connected.
*
* @param family
* The protocol family
*
* @return A new datagram channel
*
* @throws UnsupportedOperationException
* If the specified protocol family is not supported. For example,
* suppose the parameter is specified as {@link
* java.net.StandardProtocolFamily#INET6 StandardProtocolFamily.INET6}
* but IPv6 is not enabled on the platform.
* @throws IOException
* If an I/O error occurs
*
* @since 1.7
*/
public static DatagramChannel open(ProtocolFamily family) throws IOException {
return SelectorProvider.provider().openDatagramChannel(family);
}
/**
* Returns an operation set identifying this channel's supported
* operations.
@ -117,6 +195,32 @@ public abstract class DatagramChannel
// -- Socket-specific operations --
/**
* @throws AlreadyBoundException {@inheritDoc}
* @throws UnsupportedAddressTypeException {@inheritDoc}
* @throws ClosedChannelException {@inheritDoc}
* @throws IOException {@inheritDoc}
* @throws SecurityException
* If a security manager has been installed and its {@link
* SecurityManager#checkListen checkListen} method denies the
* operation
*
* @since 1.7
*/
public abstract DatagramChannel bind(SocketAddress local)
throws IOException;
/**
* @throws IllegalArgumentException {@inheritDoc}
* @throws ClosedChannelException {@inheritDoc}
* @throws IOException {@inheritDoc}
*
* @since 1.7
*/
public abstract <T> DatagramChannel setOption(SocketOption<T> name, T value)
throws IOException;
/**
* Retrieves a datagram socket associated with this channel.
*
@ -128,10 +232,10 @@ public abstract class DatagramChannel
public abstract DatagramSocket socket();
/**
* Tells whether or not this channel's socket is connected. </p>
* Tells whether or not this channel's socket is connected.
*
* @return <tt>true</tt> if, and only if, this channel's socket
* is connected
* @return {@code true} if, and only if, this channel's socket
* is {@link #isOpen open} and connected
*/
public abstract boolean isConnected();
@ -206,6 +310,19 @@ public abstract class DatagramChannel
*/
public abstract DatagramChannel disconnect() throws IOException;
/**
* Returns the remote address to which this channel's socket is connected.
*
* @return The remote address; {@code null} if the channel is not {@link
* #isOpen open} or the channel's socket is not connected
*
* @throws IOException
* If an I/O error occurs
*
* @since 1.7
*/
public abstract SocketAddress getConnectedAddress() throws IOException;
/**
* Receives a datagram via this channel.
*

View file

@ -0,0 +1,183 @@
/*
* Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package java.nio.channels;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.io.IOException;
import java.util.List;
/**
* A token representing the membership of an Internet Protocol (IP) multicast
* group.
*
* <p> A membership key may represent a membership to receive all datagrams sent
* to the group, or it may be <em>source-specific</em>, meaning that it
* represents a membership that receives only datagrams from a specific source
* address. Whether or not a membership key is source-specific may be determined
* by invoking its {@link #getSourceAddress() getSourceAddress} method.
*
* <p> A membership key is valid upon creation and remains valid until the
* membership is dropped by invoking the {@link #drop() drop} method, or
* the channel is closed. The validity of the membership key may be tested
* by invoking its {@link #isValid() isValid} method.
*
* <p> Where a membership key is not source-specific and the underlying operation
* system supports source filtering, then the {@link #block block} and {@link
* #unblock unblock} methods can be used to block or unblock multicast datagrams
* from particular source addresses.
*
* @see MulticastChannel
*
* @since 1.7
*/
public abstract class MembershipKey {
/**
* Initializes a new instance of this class.
*/
protected MembershipKey() {
}
/**
* Tells whether or not this membership is valid.
*
* <p> A multicast group membership is valid upon creation and remains
* valid until the membership is dropped by invoking the {@link #drop() drop}
* method, or the channel is closed.
*
* @return {@code true} if this membership key is valid, {@code false}
* otherwise
*/
public abstract boolean isValid();
/**
* Drop membership.
*
* <p> If the membership key represents a membership to receive all datagrams
* then the membership is dropped and the channel will no longer receive any
* datagrams sent to the group. If the membership key is source-specific
* then the channel will no longer receive datagrams sent to the group from
* that source address.
*
* <p> After membership is dropped it may still be possible to receive
* datagrams sent to the group. This can arise when datagrams are waiting to
* be received in the socket's receive buffer. After membership is dropped
* then the channel may {@link MulticastChannel#join join} the group again
* in which case a new membership key is returned.
*
* <p> Upon return, this membership object will be {@link #isValid() invalid}.
* If the multicast group membership is already invalid then invoking this
* method has no effect. Once a multicast group membership is invalid,
* it remains invalid forever.
*
* @throws IOException
* If an I/O error occurs
*/
public abstract void drop() throws IOException;
/**
* Block multicast datagrams from the given source address.
*
* <p> If this membership key is not source-specific, and the underlying
* operating system supports source filtering, then this method blocks
* multicast datagrams from the given source address. If the given source
* address is already blocked then this method has no effect.
* After a source address is blocked it may still be possible to receive
* datagams from that source. This can arise when datagrams are waiting to
* be received in the socket's receive buffer.
*
* @param source
* The source address to block
*
* @return This membership key
*
* @throws IllegalArgumentException
* If the {@code source} parameter is not a unicast address or
* is not the same address type as the multicast group
* @throws IllegalStateException
* If this membership key is source-specific or is no longer valid
* @throws UnsupportedOperationException
* If the underlying operating system does not support source
* filtering
* @throws IOException
* If an I/O error occurs
*/
public abstract MembershipKey block(InetAddress source) throws IOException;
/**
* Unblock multicast datagrams from the given source address that was
* previously blocked using the {@link #block(InetAddress) block} method.
*
* @param source
* The source address to unblock
*
* @return This membership key
*
* @throws IllegalStateException
* If the given source address is not currently blocked or the
* membership key is no longer valid
* @throws IOException
* If an I/O error occurs
*/
public abstract MembershipKey unblock(InetAddress source) throws IOException;
/**
* Returns the channel for which this membership key was created. This
* method will continue to return the channel even after the membership
* becomes {@link #isValid invalid}.
*
* @return the channel
*/
public abstract MulticastChannel getChannel();
/**
* Returns the multicast group for which this membership key was created.
* This method will continue to return the group even after the membership
* becomes {@link #isValid invalid}.
*
* @return the multicast group
*/
public abstract InetAddress getGroup();
/**
* Returns the network interface for which this membership key was created.
* This method will continue to return the network interface even after the
* membership becomes {@link #isValid invalid}.
*
* @return the network interface
*/
public abstract NetworkInterface getNetworkInterface();
/**
* Returns the source address if this membership key is source-specific,
* or {@code null} if this membership is not source-specific.
*
* @return The source address if this membership key is source-specific,
* otherwise {@code null}
*/
public abstract InetAddress getSourceAddress();
}

View file

@ -0,0 +1,211 @@
/*
* Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package java.nio.channels;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.io.IOException;
import java.net.ProtocolFamily; // javadoc
import java.net.StandardProtocolFamily; // javadoc
import java.net.StandardSocketOption; // javadoc
/**
* A network channel that supports Internet Protocol (IP) multicasting.
*
* <p> IP multicasting is the transmission of IP datagrams to members of
* a <em>group</em> that is zero or more hosts identified by a single destination
* address.
*
* <p> In the case of a channel to an {@link StandardProtocolFamily#INET IPv4} socket,
* the underlying operating system supports <a href="http://www.ietf.org/rfc/rfc2236.txt">
* <i>RFC&nbsp;2236: Internet Group Management Protocol, Version 2 (IGMPv2)</i></a>.
* It may optionally support source filtering as specified by <a
* href="http://www.ietf.org/rfc/rfc3376.txt"> <i>RFC&nbsp;3376: Internet Group
* Management Protocol, Version 3 (IGMPv3)</i></a>.
* For channels to an {@link StandardProtocolFamily#INET6 IPv6} socket, the equivalent
* standards are <a href="http://www.ietf.org/rfc/rfc2710.txt"> <i>RFC&nbsp;2710:
* Multicast Listener Discovery (MLD) for IPv6</i></a> and <a
* href="http://www.ietf.org/rfc/rfc3810.txt"> <i>RFC&nbsp;3810: Multicast Listener
* Discovery Version 2 (MLDv2) for IPv6</i></a>.
*
* <p> The {@link #join(InetAddress,NetworkInterface)} method is used to
* join a group and receive all multicast datagrams sent to the group. A channel
* may join several multicast groups and may join the same group on several
* {@link NetworkInterface interfaces}. Membership is dropped by invoking the {@link
* MembershipKey#drop drop} method on the returned {@link MembershipKey}. If the
* underlying platform supports source filtering then the {@link MembershipKey#block
* block} and {@link MembershipKey#unblock unblock} methods can be used to block or
* unblock multicast datagrams from particular source addresses.
*
* <p> The {@link #join(InetAddress,NetworkInterface,InetAddress)} method
* is used to begin receiving datagrams sent to a group whose source address matches
* a given source address. This method throws {@link UnsupportedOperationException}
* if the underlying platform does not support source filtering. Membership is
* <em>cumulative</em> and this method may be invoked again with the same group
* and interface to allow receiving datagrams from other source addresses. The
* method returns a {@link MembershipKey} that represents membership to receive
* datagrams from the given source address. Invoking the key's {@link
* MembershipKey#drop drop} method drops membership so that datagrams from the
* source address can no longer be received.
*
* <h4>Platform dependencies</h4>
*
* The multicast implementation is intended to map directly to the native
* multicasting facility. Consequently, the following items should be considered
* when developing an application that receives IP multicast datagrams:
*
* <ol>
*
* <li><p> The creation of the channel should specify the {@link ProtocolFamily}
* that corresponds to the address type of the multicast groups that the channel
* will join. There is no guarantee that a channel to a socket in one protocol
* family can join and receive multicast datagrams when the address of the
* multicast group corresponds to another protocol family. For example, it is
* implementation specific if a channel to an {@link StandardProtocolFamily#INET6 IPv6}
* socket can join an {@link StandardProtocolFamily#INET IPv4} multicast group and receive
* multicast datagrams sent to the group. </p></li>
*
* <li><p> The channel's socket should be bound to the {@link
* InetAddress#isAnyLocalAddress wildcard} address. If the socket is bound to
* a specific address, rather than the wildcard address then it is implementation
* specific if multicast datagrams are received by the socket. </p></li>
*
* <li><p> The {@link StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} option should be
* enabled prior to {@link NetworkChannel#bind binding} the socket. This is
* required to allow multiple members of the group to bind to the same
* address. </p></li>
*
* </ol>
*
* <p> <b>Usage Example:</b>
* <pre>
* // join multicast group on this interface, and also use this
* // interface for outgoing multicast datagrams
* NetworkInterface ni = NetworkInterface.getByName("hme0");
*
* DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)
* .setOption(StandardSocketOption.SO_REUSEADDR, true)
* .bind(new InetSocketAddress(5000))
* .setOption(StandardSocketOption.IP_MULTICAST_IF, ni);
*
* InetAddress group = InetAddress.getByName("225.4.5.6");
*
* MembershipKey key = dc.join(group, ni);
* </pre>
*
* @since 1.7
*/
public interface MulticastChannel
extends NetworkChannel
{
/**
* Joins a multicast group to begin receiving all datagrams sent to the group,
* returning a membership key.
*
* <p> If this channel is currently a member of the group on the given
* interface to receive all datagrams then the membership key, representing
* that membership, is returned. Otherwise this channel joins the group and
* the resulting new membership key is returned. The resulting membership key
* is not {@link MembershipKey#getSourceAddress source-specific}.
*
* <p> A multicast channel may join several multicast groups, including
* the same group on more than one interface. An implementation may impose a
* limit on the number of groups that may be joined at the same time.
*
* @param group
* The multicast address to join
* @param interf
* The network interface on which to join the group
*
* @return The membership key
*
* @throws IllegalArgumentException
* If the group parameter is not a {@link InetAddress#isMulticastAddress
* multicast} address, or the group parameter is an address type
* that is not supported by this channel
* @throws IllegalStateException
* If the channel already has source-specific membership of the
* group on the interface
* @throws ClosedChannelException
* If this channel is closed
* @throws IOException
* If an I/O error occurs
* @throws SecurityException
* If a security manager is set, and its
* {@link SecurityManager#checkMulticast(InetAddress) checkMulticast}
* method denies access to the multiast group
*/
MembershipKey join(InetAddress group, NetworkInterface interf)
throws IOException;
/**
* Joins a multicast group to begin receiving datagrams sent to the group
* from a given source address.
*
* <p> If this channel is currently a member of the group on the given
* interface to receive datagrams from the given source address then the
* membership key, representing that membership, is returned. Otherwise this
* channel joins the group and the resulting new membership key is returned.
* The resulting membership key is {@link MembershipKey#getSourceAddress
* source-specific}.
*
* <p> Membership is <em>cumulative</em> and this method may be invoked
* again with the same group and interface to allow receiving datagrams sent
* by other source addresses to the group.
*
* @param group
* The multicast address to join
* @param interf
* The network interface on which to join the group
* @param source
* The source address
*
* @return The membership key
*
* @throws IllegalArgumentException
* If the group parameter is not a {@link
* InetAddress#isMulticastAddress multicast} address, the
* source parameter is not a unicast address, the group
* parameter is an address type that is not supported by this channel,
* or the source parameter is not the same address type as the group
* @throws IllegalStateException
* If the channel is currently a member of the group on the given
* interface to receive all datagrams
* @throws UnsupportedOperationException
* If the underlying operation system does not support source filtering
* @throws ClosedChannelException
* If this channel is closed
* @throws IOException
* If an I/O error occurs
* @throws SecurityException
* If a security manager is set, and its
* {@link SecurityManager#checkMulticast(InetAddress) checkMulticast}
* method denies access to the multiast group
*/
MembershipKey join(InetAddress group, NetworkInterface interf, InetAddress source)
throws IOException;
}

View file

@ -0,0 +1,158 @@
/*
* Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package java.nio.channels;
import java.net.SocketOption;
import java.net.SocketAddress;
import java.util.Set;
import java.io.IOException;
/**
* A channel to a network socket.
*
* <p> A channel that implements this interface is a channel to a network
* socket. The {@link #bind(SocketAddress) bind} method is used to bind the
* socket to a local {@link SocketAddress address}, the {@link #getLocalAddress()
* getLocalAddress} method returns the address that the socket is bound to, and
* the {@link #setOption(SocketOption,Object) setOption} and {@link
* #getOption(SocketOption) getOption} methods are used to set and query socket
* options. An implementation of this interface should specify the socket options
* that it supports.
*
* <p> The {@link #bind bind} and {@link #setOption setOption} methods that do
* not otherwise have a value to return are specified to return the network
* channel upon which they are invoked. This allows method invocations to be
* chained. Implementations of this interface should specialize the return type
* so that method invocations on the implementation class can be chained.
*
* @since 1.7
*/
public interface NetworkChannel
extends Channel
{
/**
* Binds the channel's socket to a local address.
*
* <p> This method is used to establish an association between the socket and
* a local address. Once an association is established then the socket remains
* bound until the channel is closed. If the {@code local} parameter has the
* value {@code null} then the socket will be bound to an address that is
* assigned automatically.
*
* @param local
* The address to bind the socket, or {@code null} to bind the socket
* to an automatically assigned socket address
*
* @return This channel
*
* @throws AlreadyBoundException
* If the socket is already bound
* @throws UnsupportedAddressTypeException
* If the type of the given address is not supported
* @throws ClosedChannelException
* If the channel is closed
* @throws IOException
* If some other I/O error occurs
* @throws SecurityException
* If a security manager is installed and it denies an unspecified
* permission. An implementation of this interface should specify
* any required permissions.
*
* @see #getLocalAddress
*/
NetworkChannel bind(SocketAddress local) throws IOException;
/**
* Returns the socket address that this channel's socket is bound to, or
* {@code null} if the socket is not bound.
*
* <p> Where the channel is {@link #bind bound} to an Internet Protocol
* socket address then the return value from this method is of type {@link
* java.net.InetSocketAddress}.
*
* @return The socket address that the socket is bound to, or {@code null}
* if the channel is not {@link #isOpen open} or the channel's socket
* is not bound
*
* @throws IOException
* If an I/O error occurs
*/
SocketAddress getLocalAddress() throws IOException;
/**
* Sets the value of a socket option.
*
* @param name
* The socket option
* @param value
* The value of the socket option. A value of {@code null} may be
* a valid value for some socket options.
*
* @return This channel
*
* @throws IllegalArgumentException
* If the socket option is not supported by this channel, or
* the value is not a valid value for this socket option
* @throws ClosedChannelException
* If this channel is closed
* @throws IOException
* If an I/O error occurs
*
* @see java.net.StandardSocketOption
*/
<T> NetworkChannel setOption(SocketOption<T> name, T value) throws IOException;
/**
* Returns the value of a socket option.
*
* @param name
* The socket option
*
* @return The value of the socket option. A value of {@code null} may be
* a valid value for some socket options.
*
* @throws IllegalArgumentException
* If the socket option is not supported by this channel
* @throws ClosedChannelException
* If this channel is closed
* @throws IOException
* If an I/O error occurs
*
* @see java.net.StandardSocketOption
*/
<T> T getOption(SocketOption<T> name) throws IOException;
/**
* Returns a set of the socket options supported by this channel.
*
* <p> This method will continue to return the set of options even after the
* channel has been closed.
*
* @return A set of the socket options supported by this channel
*/
Set<SocketOption<?>> options();
}

View file

@ -27,33 +27,44 @@ package java.nio.channels;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.SocketOption;
import java.net.SocketAddress;
import java.nio.channels.spi.*;
/**
* A selectable channel for stream-oriented listening sockets.
*
* <p> Server-socket channels are not a complete abstraction of listening
* network sockets. Binding and the manipulation of socket options must be
* done through an associated {@link java.net.ServerSocket} object obtained by
* invoking the {@link #socket() socket} method. It is not possible to create
* a channel for an arbitrary, pre-existing server socket, nor is it possible
* to specify the {@link java.net.SocketImpl} object to be used by a server
* socket associated with a server-socket channel.
*
* <p> A server-socket channel is created by invoking the {@link #open() open}
* method of this class. A newly-created server-socket channel is open but not
* yet bound. An attempt to invoke the {@link #accept() accept} method of an
* unbound server-socket channel will cause a {@link NotYetBoundException} to
* be thrown. A server-socket channel can be bound by invoking one of the
* {@link java.net.ServerSocket#bind(java.net.SocketAddress,int) bind} methods
* of an associated server socket.
* method of this class. It is not possible to create a channel for an arbitrary,
* pre-existing {@link ServerSocket}. A newly-created server-socket channel is
* open but not yet bound. An attempt to invoke the {@link #accept() accept}
* method of an unbound server-socket channel will cause a {@link NotYetBoundException}
* to be thrown. A server-socket channel can be bound by invoking one of the
* {@link #bind(java.net.SocketAddress,int) bind} methods defined by this class.
*
* <p> Socket options are configured using the {@link #setOption(SocketOption,Object)
* setOption} method. Server-socket channels support the following options:
* <blockquote>
* <table border>
* <tr>
* <th>Option Name</th>
* <th>Description</th>
* </tr>
* <tr>
* <td> {@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} </td>
* <td> The size of the socket receive buffer </td>
* </tr>
* <tr>
* <td> {@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} </td>
* <td> Re-use address </td>
* </tr>
* </table>
* </blockquote>
* Additional (implementation specific) options may also be supported.
*
* <p> Server-socket channels are safe for use by multiple concurrent threads.
* </p>
*
*
* @author Mark Reinhold
* @author JSR-51 Expert Group
* @since 1.4
@ -61,6 +72,7 @@ import java.nio.channels.spi.*;
public abstract class ServerSocketChannel
extends AbstractSelectableChannel
implements NetworkChannel
{
/**
@ -109,6 +121,89 @@ public abstract class ServerSocketChannel
// -- ServerSocket-specific operations --
/**
* Binds the channel's socket to a local address and configures the socket
* to listen for connections.
*
* <p> An invocation of this method is equivalent to the following:
* <blockquote><pre>
* bind(local, 0);
* </pre></blockquote>
*
* @param local
* The local address to bind the socket, or {@code null} to bind
* to an automatically assigned socket address
*
* @return This channel
*
* @throws AlreadyBoundException {@inheritDoc}
* @throws UnsupportedAddressTypeException {@inheritDoc}
* @throws ClosedChannelException {@inheritDoc}
* @throws IOException {@inheritDoc}
* @throws SecurityException
* If a security manager has been installed and its {@link
* SecurityManager#checkListen checkListen} method denies the
* operation
*
* @since 1.7
*/
public final ServerSocketChannel bind(SocketAddress local)
throws IOException
{
return bind(local, 0);
}
/**
* Binds the channel's socket to a local address and configures the socket to
* listen for connections.
*
* <p> This method is used to establish an association between the socket and
* a local address. Once an association is established then the socket remains
* bound until the channel is closed.
*
* <p> The {@code backlog} parameter is the maximum number of pending
* connections on the socket. Its exact semantics are implementation specific.
* In particular, an implementation may impose a maximum length or may choose
* to ignore the parameter altogther. If the {@code backlog} parameter has
* the value {@code 0}, or a negative value, then an implementation specific
* default is used.
*
* @param local
* The address to bind the socket, or {@code null} to bind to an
* automatically assigned socket address
* @param backlog
* The maximum number of pending connections
*
* @return This channel
*
* @throws AlreadyBoundException
* If the socket is already bound
* @throws UnsupportedAddressTypeException
* If the type of the given address is not supported
* @throws ClosedChannelException
* If this channel is closed
* @throws IOException
* If some other I/O error occurs
* @throws SecurityException
* If a security manager has been installed and its {@link
* SecurityManager#checkListen checkListen} method denies the
* operation
*
* @since 1.7
*/
public abstract ServerSocketChannel bind(SocketAddress local, int backlog)
throws IOException;
/**
* @throws IllegalArgumentException {@inheritDoc}
* @throws ClosedChannelException {@inheritDoc}
* @throws IOException {@inheritDoc}
*
* @since 1.7
*/
public abstract <T> ServerSocketChannel setOption(SocketOption<T> name, T value)
throws IOException;
/**
* Retrieves a server socket associated with this channel.
*

View file

@ -27,24 +27,17 @@ package java.nio.channels;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketOption;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.spi.*;
/**
* A selectable channel for stream-oriented connecting sockets.
*
* <p> Socket channels are not a complete abstraction of connecting network
* sockets. Binding, shutdown, and the manipulation of socket options must be
* done through an associated {@link java.net.Socket} object obtained by
* invoking the {@link #socket() socket} method. It is not possible to create
* a channel for an arbitrary, pre-existing socket, nor is it possible to
* specify the {@link java.net.SocketImpl} object to be used by a socket
* associated with a socket channel.
*
* <p> A socket channel is created by invoking one of the {@link #open open}
* methods of this class. A newly-created socket channel is open but not yet
* methods of this class. It is not possible to create a channel for an arbitrary,
* pre-existing socket. A newly-created socket channel is open but not yet
* connected. An attempt to invoke an I/O operation upon an unconnected
* channel will cause a {@link NotYetConnectedException} to be thrown. A
* socket channel can be connected by invoking its {@link #connect connect}
@ -59,16 +52,6 @@ import java.nio.channels.spi.*;
* Whether or not a connection operation is in progress may be determined by
* invoking the {@link #isConnectionPending isConnectionPending} method.
*
* <p> The input and output sides of a socket channel may independently be
* <i>shut down</i> without actually closing the channel. Shutting down the
* input side of a channel by invoking the {@link java.net.Socket#shutdownInput
* shutdownInput} method of an associated socket object will cause further
* reads on the channel to return <tt>-1</tt>, the end-of-stream indication.
* Shutting down the output side of the channel by invoking the {@link
* java.net.Socket#shutdownOutput shutdownOutput} method of an associated
* socket object will cause further writes on the channel to throw a {@link
* ClosedChannelException}.
*
* <p> Socket channels support <i>asynchronous shutdown,</i> which is similar
* to the asynchronous close operation specified in the {@link Channel} class.
* If the input side of a socket is shut down by one thread while another
@ -79,6 +62,43 @@ import java.nio.channels.spi.*;
* channel, then the blocked thread will receive an {@link
* AsynchronousCloseException}.
*
* <p> Socket options are configured using the {@link #setOption(SocketOption,Object)
* setOption} method. Socket channels support the following options:
* <blockquote>
* <table border>
* <tr>
* <th>Option Name</th>
* <th>Description</th>
* </tr>
* <tr>
* <td> {@link java.net.StandardSocketOption#SO_SNDBUF SO_SNDBUF} </td>
* <td> The size of the socket send buffer </td>
* </tr>
* <tr>
* <td> {@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} </td>
* <td> The size of the socket receive buffer </td>
* </tr>
* <tr>
* <td> {@link java.net.StandardSocketOption#SO_KEEPALIVE SO_KEEPALIVE} </td>
* <td> Keep connection alive </td>
* </tr>
* <tr>
* <td> {@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} </td>
* <td> Re-use address </td>
* </tr>
* <tr>
* <td> {@link java.net.StandardSocketOption#SO_LINGER SO_LINGER} </td>
* <td> Linger on close if data is present (when configured in blocking mode
* only) </td>
* </tr>
* <tr>
* <td> {@link java.net.StandardSocketOption#TCP_NODELAY TCP_NODELAY} </td>
* <td> Disable the Nagle algorithm </td>
* </tr>
* </table>
* </blockquote>
* Additional (implementation specific) options may also be supported.
*
* <p> Socket channels are safe for use by multiple concurrent threads. They
* support concurrent reading and writing, though at most one thread may be
* reading and at most one thread may be writing at any given time. The {@link
@ -87,7 +107,6 @@ import java.nio.channels.spi.*;
* or write operation while an invocation of one of these methods is in
* progress will block until that invocation is complete. </p>
*
*
* @author Mark Reinhold
* @author JSR-51 Expert Group
* @since 1.4
@ -95,7 +114,7 @@ import java.nio.channels.spi.*;
public abstract class SocketChannel
extends AbstractSelectableChannel
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel
{
/**
@ -191,6 +210,73 @@ public abstract class SocketChannel
// -- Socket-specific operations --
/**
* @throws ConnectionPendingException
* If a non-blocking connection operation is already in progress on
* this channel
* @throws AlreadyBoundException {@inheritDoc}
* @throws UnsupportedAddressTypeException {@inheritDoc}
* @throws ClosedChannelException {@inheritDoc}
* @throws IOException {@inheritDoc}
*
* @since 1.7
*/
@Override
public abstract SocketChannel bind(SocketAddress local)
throws IOException;
/**
* @throws IllegalArgumentException {@inheritDoc}
* @throws ClosedChannelException {@inheritDoc}
* @throws IOException {@inheritDoc}
*
* @since 1.7
*/
@Override
public abstract <T> SocketChannel setOption(SocketOption<T> name, T value)
throws IOException;
/**
* Shutdown the connection for reading without closing the channel.
*
* <p> Once shutdown for reading then further reads on the channel will
* return {@code -1}, the end-of-stream indication. If the input side of the
* connection is already shutdown then invoking this method has no effect.
*
* @return The channel
*
* @throws NotYetConnectedException
* If this channel is not yet connected
* @throws ClosedChannelException
* If this channel is closed
* @throws IOException
* If some other I/O error occurs
*
* @since 1.7
*/
public abstract SocketChannel shutdownInput() throws IOException;
/**
* Shutdown the connection for writing without closing the channel.
*
* <p> Once shutdown for writing then further attempts to write to the
* channel will throw {@link ClosedChannelException}. If the output side of
* the connection is already shutdown then invoking this method has no
* effect.
*
* @return The channel
*
* @throws NotYetConnectedException
* If this channel is not yet connected
* @throws ClosedChannelException
* If this channel is closed
* @throws IOException
* If some other I/O error occurs
*
* @since 1.7
*/
public abstract SocketChannel shutdownOutput() throws IOException;
/**
* Retrieves a socket associated with this channel.
*
@ -202,10 +288,10 @@ public abstract class SocketChannel
public abstract Socket socket();
/**
* Tells whether or not this channel's network socket is connected. </p>
* Tells whether or not this channel's network socket is connected.
*
* @return <tt>true</tt> if, and only if, this channel's network socket
* is connected
* is {@link #isOpen open} and connected
*/
public abstract boolean isConnected();
@ -339,6 +425,22 @@ public abstract class SocketChannel
*/
public abstract boolean finishConnect() throws IOException;
/**
* Returns the remote address to which this channel's socket is connected.
*
* <p> Where the channel is bound and connected to an Internet Protocol
* socket address then the return value from this method is of type {@link
* java.net.InetSocketAddress}.
*
* @return The remote address; {@code null} if the channel is not {@link
* #isOpen open} or the channel's socket is not connected
*
* @throws IOException
* If an I/O error occurs
*
* @since 1.7
*/
public abstract SocketAddress getConnectedAddress() throws IOException;
// -- ByteChannel operations --

View file

@ -146,3 +146,14 @@ gen OverlappingFileLockException "
* virtual machine, or when another thread is already waiting to lock an
* overlapping region of the same file." \
2047812138163068433L
SINCE=1.7
SUPER=IllegalStateException
gen AlreadyBoundException "
* Unchecked exception thrown when an attempt is made to bind the socket a
* network oriented channel that is already bound." \
6796072983322737592L

View file

@ -0,0 +1,231 @@
/*
* Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/**
* Defines channels, which represent connections to entities that are capable of
* performing I/O operations, such as files and sockets; defines selectors, for
* multiplexed, non-blocking I/O operations.
*
* <a name="channels"></a>
*
* <blockquote><table cellspacing=1 cellpadding=0 summary="Lists channels and their descriptions">
* <tr><th><p align="left">Channels</p></th><th><p align="left">Description</p></th></tr>
* <tr><td valign=top><tt><i>{@link java.nio.channels.Channel}</i></tt></td>
* <td>A nexus for I/O operations</td></tr>
* <tr><td valign=top><tt>&nbsp;&nbsp;<i>{@link java.nio.channels.ReadableByteChannel}</i></tt></td>
* <td>Can read into a buffer</td></tr>
* <tr><td valign=top><tt>&nbsp;&nbsp;&nbsp;&nbsp;<i>{@link java.nio.channels.ScatteringByteChannel}&nbsp;&nbsp;</i></tt></td>
* <td>Can read into a sequence of&nbsp;buffers</td></tr>
* <tr><td valign=top><tt>&nbsp;&nbsp;<i>{@link java.nio.channels.WritableByteChannel}</i></tt></td>
* <td>Can write from a buffer</td></tr>
* <tr><td valign=top><tt>&nbsp;&nbsp;&nbsp;&nbsp;<i>{@link java.nio.channels.GatheringByteChannel}</i></tt></td>
* <td>Can write from a sequence of&nbsp;buffers</td></tr>
* <tr><td valign=top><tt>&nbsp;&nbsp;<i>{@link java.nio.channels.ByteChannel}</i></tt></td>
* <td>Can read/write to/from a&nbsp;buffer</td></tr>
* <tr><td valign=top><tt>&nbsp;&nbsp;&nbsp;&nbsp;<i>{@link java.nio.channels.SeekableByteChannel}</i></tt></td>
* <td>A {@code ByteChannel} connected to an entity that contains a variable-length sequence of bytes</td></tr>
* <tr><td valign=top><tt>&nbsp;&nbsp;<i>{@link java.nio.channels.NetworkChannel}</i></tt></td>
* <td>A channel to a network socket</td></tr>
* <tr><td valign=top><tt>&nbsp;&nbsp;&nbsp;&nbsp;<i>{@link java.nio.channels.MulticastChannel}</i></tt></td>
* <td>Can join Internet Protocol (IP) multicast groups</td></tr>
* <tr><td valign=top><tt>{@link java.nio.channels.Channels}</tt></td>
* <td>Utility methods for channel/stream interoperation</td></tr>
* </table></blockquote>
*
* <p> A <i>channel</i> represents an open connection to an entity such as a
* hardware device, a file, a network socket, or a program component that is
* capable of performing one or more distinct I/O operations, for example reading
* or writing. As specified in the {@link java.nio.channels.Channel} interface,
* channels are either open or closed, and they are both <i>asynchronously
* closeable</i> and <i>interruptible</i>.
*
* <p> The {@link java.nio.channels.Channel} interface is extended by several
* other interfaces.
*
* <p> The {@link java.nio.channels.ReadableByteChannel} interface specifies a
* {@link java.nio.channels.ReadableByteChannel#read read} method that reads bytes
* from the channel into a buffer; similarly, the {@link
* java.nio.channels.WritableByteChannel} interface specifies a {@link
* java.nio.channels.WritableByteChannel#write write} method that writes bytes
* from a buffer to the channel. The {@link java.nio.channels.ByteChannel}
* interface unifies these two interfaces for the common case of channels that can
* both read and write bytes. The {@link java.nio.channels.SeekableByteChannel}
* interface extends the {@code ByteChannel} interface with methods to {@link
* java.nio.channels.SeekableByteChannel#position() query} and {@link
* java.nio.channels.SeekableByteChannel#position(long) modify} the channel's
* current position, and its {@link java.nio.channels.SeekableByteChannel#size
* size}.
*
* <p> The {@link java.nio.channels.ScatteringByteChannel} and {@link
* java.nio.channels.GatheringByteChannel} interfaces extend the {@link
* java.nio.channels.ReadableByteChannel} and {@link
* java.nio.channels.WritableByteChannel} interfaces, respectively, adding {@link
* java.nio.channels.ScatteringByteChannel#read read} and {@link
* java.nio.channels.GatheringByteChannel#write write} methods that take a
* sequence of buffers rather than a single buffer.
*
* <p> The {@link java.nio.channels.NetworkChannel} interface specifies methods
* to {@link java.nio.channels.NetworkChannel#bind bind} the channel's socket,
* obtain the address to which the socket is bound, and methods to {@link
* java.nio.channels.NetworkChannel#getOption get} and {@link
* java.nio.channels.NetworkChannel#setOption set} socket options. The {@link
* java.nio.channels.MulticastChannel} interface specifies methods to join
* Internet Protocol (IP) multicast groups.
*
* <p> The {@link java.nio.channels.Channels} utility class defines static methods
* that support the interoperation of the stream classes of the <tt>{@link
* java.io}</tt> package with the channel classes of this package. An appropriate
* channel can be constructed from an {@link java.io.InputStream} or an {@link
* java.io.OutputStream}, and conversely an {@link java.io.InputStream} or an
* {@link java.io.OutputStream} can be constructed from a channel. A {@link
* java.io.Reader} can be constructed that uses a given charset to decode bytes
* from a given readable byte channel, and conversely a {@link java.io.Writer} can
* be constructed that uses a given charset to encode characters into bytes and
* write them to a given writable byte channel.
*
* <blockquote><table cellspacing=1 cellpadding=0 summary="Lists file channels and their descriptions">
* <tr><th><p align="left">File channels</p></th><th><p align="left">Description</p></th></tr>
* <tr><td valign=top><tt>{@link java.nio.channels.FileChannel}</tt></td>
* <td>Reads, writes, maps, and manipulates files</td></tr>
* <tr><td valign=top><tt>{@link java.nio.channels.FileLock}</tt></td>
* <td>A lock on a (region of a) file</td></tr>
* <tr><td valign=top><tt>{@link java.nio.MappedByteBuffer}/{@link java.nio.MappedBigByteBuffer}&nbsp;&nbsp;</tt></td>
* <td>A direct byte buffer or big byte buffer mapped to a region of a&nbsp;file</td></tr>
* </table></blockquote>
*
* <p> The {@link java.nio.channels.FileChannel} class supports the usual
* operations of reading bytes from, and writing bytes to, a channel connected to
* a file, as well as those of querying and modifying the current file position
* and truncating the file to a specific size. It defines methods for acquiring
* locks on the whole file or on a specific region of a file; these methods return
* instances of the {@link java.nio.channels.FileLock} class. Finally, it defines
* methods for forcing updates to the file to be written to the storage device that
* contains it, for efficiently transferring bytes between the file and other
* channels, and for mapping a region of the file directly into memory.
*
* <p> A {@code FileChannel} is created by invoking one of its static {@link
* java.nio.channels.FileChannel#open open} methods, or by invoking the {@code
* getChannel} method of a {@link java.io.FileInputStream}, {@link
* java.io.FileOutputStream}, or {@link java.io.RandomAccessFile} to return a
* file channel connected to the same underlying file as the <tt>{@link java.io}</tt>
* class.
*
* <a name="multiplex"></a>
* <blockquote><table cellspacing=1 cellpadding=0 summary="Lists multiplexed, non-blocking channels and their descriptions">
* <tr><th><p align="left">Multiplexed, non-blocking I/O</p></th><th><p align="left">Description</p></th></tr>
* <tr><td valign=top><tt>{@link java.nio.channels.SelectableChannel}</tt></td>
* <td>A channel that can be multiplexed</td></tr>
* <tr><td valign=top><tt>&nbsp;&nbsp;{@link java.nio.channels.DatagramChannel}</tt></td>
* <td>A channel to a datagram-oriented socket</td></tr>
* <tr><td valign=top><tt>&nbsp;&nbsp;{@link java.nio.channels.Pipe.SinkChannel}</tt></td>
* <td>The write end of a pipe</td></tr>
* <tr><td valign=top><tt>&nbsp;&nbsp;{@link java.nio.channels.Pipe.SourceChannel}</tt></td>
* <td>The read end of a pipe</td></tr>
* <tr><td valign=top><tt>&nbsp;&nbsp;{@link java.nio.channels.ServerSocketChannel}&nbsp;&nbsp;</tt></td>
* <td>A channel to a stream-oriented listening socket</td></tr>
* <tr><td valign=top><tt>&nbsp;&nbsp;{@link java.nio.channels.SocketChannel}</tt></td>
* <td>A channel for a stream-oriented connecting socket</td></tr>
* <tr><td valign=top><tt>{@link java.nio.channels.Selector}</tt></td>
* <td>A multiplexor of selectable channels</td></tr>
* <tr><td valign=top><tt>{@link java.nio.channels.SelectionKey}</tt></td>
* <td>A token representing the registration <br> of a channel
* with&nbsp;a&nbsp;selector</td></tr>
* <tr><td valign=top><tt>{@link java.nio.channels.Pipe}</tt></td>
* <td>Two channels that form a unidirectional&nbsp;pipe</td></tr>
* </table></blockquote>
*
* <p> Multiplexed, non-blocking I/O, which is much more scalable than
* thread-oriented, blocking I/O, is provided by <i>selectors</i>, <i>selectable
* channels</i>, and <i>selection keys</i>.
*
* <p> A <a href="Selector.html"><i>selector</i></a> is a multiplexor of <a
* href="SelectableChannel.html"><i>selectable channels</i></a>, which in turn are
* a special type of channel that can be put into <a
* href="SelectableChannel.html#bm"><i>non-blocking mode</i></a>. To perform
* multiplexed I/O operations, one or more selectable channels are first created,
* put into non-blocking mode, and {@link
* java.nio.channels.SelectableChannel#register <i>registered</i>}
* with a selector. Registering a channel specifies the set of I/O operations
* that will be tested for readiness by the selector, and returns a <a
* href="SelectionKey.html"><i>selection key</i></a> that represents the
* registration.
*
* <p> Once some channels have been registered with a selector, a <a
* href="Selector.html#selop"><i>selection operation</i></a> can be performed in
* order to discover which channels, if any, have become ready to perform one or
* more of the operations in which interest was previously declared. If a channel
* is ready then the key returned when it was registered will be added to the
* selector's <i>selected-key set</i>. The key set, and the keys within it, can
* be examined in order to determine the operations for which each channel is
* ready. From each key one can retrieve the corresponding channel in order to
* perform whatever I/O operations are required.
*
* <p> That a selection key indicates that its channel is ready for some operation
* is a hint, but not a guarantee, that such an operation can be performed by a
* thread without causing the thread to block. It is imperative that code that
* performs multiplexed I/O be written so as to ignore these hints when they prove
* to be incorrect.
*
* <p> This package defines selectable-channel classes corresponding to the {@link
* java.net.DatagramSocket}, {@link java.net.ServerSocket}, and {@link
* java.net.Socket} classes defined in the <tt>{@link java.net}</tt> package.
* Minor changes to these classes have been made in order to support sockets that
* are associated with channels. This package also defines a simple class that
* implements unidirectional pipes. In all cases, a new selectable channel is
* created by invoking the static <tt>open</tt> method of the corresponding class.
* If a channel needs an associated socket then a socket will be created as a side
* effect of this operation.
*
* <p> The implementation of selectors, selectable channels, and selection keys
* can be replaced by "plugging in" an alternative definition or instance of the
* {@link java.nio.channels.spi.SelectorProvider} class defined in the <tt>{@link
* java.nio.channels.spi}</tt> package. It is not expected that many developers
* will actually make use of this facility; it is provided primarily so that
* sophisticated users can take advantage of operating-system-specific
* I/O-multiplexing mechanisms when very high performance is required.
*
* <p> Much of the bookkeeping and synchronization required to implement the
* multiplexed-I/O abstractions is performed by the {@link
* java.nio.channels.spi.AbstractInterruptibleChannel}, {@link
* java.nio.channels.spi.AbstractSelectableChannel}, {@link
* java.nio.channels.spi.AbstractSelectionKey}, and {@link
* java.nio.channels.spi.AbstractSelector} classes in the <tt>{@link
* java.nio.channels.spi}</tt> package. When defining a custom selector provider,
* only the {@link java.nio.channels.spi.AbstractSelector} and {@link
* java.nio.channels.spi.AbstractSelectionKey} classes should be subclassed
* directly; custom channel classes should extend the appropriate {@link
* java.nio.channels.SelectableChannel} subclasses defined in this package.
*
* <hr width="80%">
* <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
* or method in any class or interface in this package will cause a {@link
* java.lang.NullPointerException NullPointerException} to be thrown.
*
* @since 1.4
* @author Mark Reinhold
* @author JSR-51 Expert Group
*/
package java.nio.channels;

View file

@ -1,222 +0,0 @@
<!--
Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 only, as
published by the Free Software Foundation. Sun designates this
particular file as subject to the "Classpath" exception as provided
by Sun in the LICENSE file that accompanied this code.
This code 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
version 2 for more details (a copy is included in the LICENSE file that
accompanied this code).
You should have received a copy of the GNU General Public License version
2 along with this work; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
CA 95054 USA or visit www.sun.com if you need additional information or
have any questions.
-->
<!doctype html public "-//IETF//DTD HTML//EN">
<html>
<body bgcolor="white">
Defines channels, which represent connections to entities that are capable of
performing I/O operations, such as files and sockets; defines selectors, for
multiplexed, non-blocking I/O operations.
<a name="channels">
<blockquote><table cellspacing=1 cellpadding=0 summary="Lists channels and their descriptions">
<tr><th><p align="left">Channels</p></th><th><p align="left">Description</p></th></tr>
<tr><td valign=top><tt><i>{@link java.nio.channels.Channel}</i></tt></td>
<td>A nexus for I/O operations</td></tr>
<tr><td valign=top><tt>&nbsp;&nbsp;<i>{@link java.nio.channels.ReadableByteChannel}</i></tt></td>
<td>Can read into a buffer</td></tr>
<tr><td valign=top><tt>&nbsp;&nbsp;&nbsp;&nbsp;<i>{@link java.nio.channels.ScatteringByteChannel}&nbsp;&nbsp;</i></tt></td>
<td>Can read into a sequence of&nbsp;buffers</td></tr>
<tr><td valign=top><tt>&nbsp;&nbsp;<i>{@link java.nio.channels.WritableByteChannel}</i></tt></td>
<td>Can write from a buffer</td></tr>
<tr><td valign=top><tt>&nbsp;&nbsp;&nbsp;&nbsp;<i>{@link java.nio.channels.GatheringByteChannel}</i></tt></td>
<td>Can write from a sequence of&nbsp;buffers</td></tr>
<tr><td valign=top><tt>&nbsp;&nbsp;<i>{@link java.nio.channels.ByteChannel}</i></tt></td>
<td>Can read/write to/from a&nbsp;buffer</td></tr>
<tr><td valign=top><tt>{@link java.nio.channels.Channels}</tt></td>
<td>Utility methods for channel/stream interoperation</td></tr>
</table></blockquote>
<p> A <i>channel</i> represents an open connection to an entity such as a
hardware device, a file, a network socket, or a program component that is
capable of performing one or more distinct I/O operations, for example reading
or writing. As specified in the {@link java.nio.channels.Channel} interface,
channels are either open or closed, and they are both <i>asynchronously
closeable</i> and <i>interruptible</i>.
<p> The {@link java.nio.channels.Channel} interface is extended by several
other interfaces, each of which specifies a new I/O operation.
<p> The {@link java.nio.channels.ReadableByteChannel} interface specifies a
{@link java.nio.channels.ReadableByteChannel#read read} method that reads bytes
from the channel into a buffer; similarly, the {@link
java.nio.channels.WritableByteChannel} interface specifies a {@link
java.nio.channels.WritableByteChannel#write write} method that writes bytes
from a buffer to the channel. The {@link java.nio.channels.ByteChannel}
interface unifies these two interfaces for the common case of channels that can
both read and write bytes.
<p> The {@link java.nio.channels.ScatteringByteChannel} and {@link
java.nio.channels.GatheringByteChannel} interfaces extend the {@link
java.nio.channels.ReadableByteChannel} and {@link
java.nio.channels.WritableByteChannel} interfaces, respectively, adding {@link
java.nio.channels.ScatteringByteChannel#read read} and {@link
java.nio.channels.GatheringByteChannel#write write} methods that take a
sequence of buffers rather than a single buffer.
<p> The {@link java.nio.channels.Channels} utility class defines static methods
that support the interoperation of the stream classes of the <tt>{@link
java.io}</tt> package with the channel classes of this package. An appropriate
channel can be constructed from an {@link java.io.InputStream} or an {@link
java.io.OutputStream}, and conversely an {@link java.io.InputStream} or an
{@link java.io.OutputStream} can be constructed from a channel. A {@link
java.io.Reader} can be constructed that uses a given charset to decode bytes
from a given readable byte channel, and conversely a {@link java.io.Writer} can
be constructed that uses a given charset to encode characters into bytes and
write them to a given writable byte channel.
<blockquote><table cellspacing=1 cellpadding=0 summary="Lists file channels and their descriptions">
<tr><th><p align="left">File channels</p></th><th><p align="left">Description</p></th></tr>
<tr><td valign=top><tt>{@link java.nio.channels.FileChannel}</tt></td>
<td>Reads, writes, maps, and manipulates files</td></tr>
<tr><td valign=top><tt>{@link java.nio.channels.FileLock}</tt></td>
<td>A lock on a (region of a) file</td></tr>
<tr><td valign=top><tt>{@link java.nio.MappedByteBuffer}&nbsp;&nbsp;</tt></td>
<td>A direct byte buffer mapped to a region of a&nbsp;file</td></tr>
</table></blockquote>
<p> The {@link java.nio.channels.FileChannel} class supports the usual
operations of reading bytes from, and writing bytes to, a channel connected to
a file, as well as those of querying and modifying the current file position
and truncating the file to a specific size. It defines methods for acquiring
locks on the whole file or on a specific region of a file; these methods return
instances of the {@link java.nio.channels.FileLock} class. Finally, it defines
methods for forcing updates to the file to be written to the storage device that
contains it, for efficiently transferring bytes between the file and other
channels, and for mapping a region of the file directly into memory. This last
operation creates an instance of the {@link java.nio.MappedByteBuffer}
class, which extends the {@link java.nio.ByteBuffer} class with several
file-related operations.
<p> A <tt>getChannel</tt> method has been added to each of the {@link
java.io.FileInputStream#getChannel FileInputStream}, {@link
java.io.FileOutputStream#getChannel FileOutputStream}, and {@link
java.io.RandomAccessFile#getChannel RandomAccessFile} classes of the <tt>{@link
java.io java.io}</tt> package. Invoking this method upon an instance of one of
these classes will return a file channel connected to the underlying file.
<a name="multiplex">
<blockquote><table cellspacing=1 cellpadding=0 summary="Lists multiplexed, non-blocking channels and their descriptions">
<tr><th><p align="left">Multiplexed, non-blocking I/O</p></th><th><p align="left">Description</p></th></tr>
<tr><td valign=top><tt>{@link java.nio.channels.SelectableChannel}</tt></td>
<td>A channel that can be multiplexed</td></tr>
<tr><td valign=top><tt>&nbsp;&nbsp;{@link java.nio.channels.DatagramChannel}</tt></td>
<td>A channel for a {@link java.net.DatagramSocket java.net.DatagramSocket}</td></tr>
<tr><td valign=top><tt>&nbsp;&nbsp;{@link java.nio.channels.Pipe.SinkChannel}</tt></td>
<td>The write end of a pipe</td></tr>
<tr><td valign=top><tt>&nbsp;&nbsp;{@link java.nio.channels.Pipe.SourceChannel}</tt></td>
<td>The read end of a pipe</td></tr>
<tr><td valign=top><tt>&nbsp;&nbsp;{@link java.nio.channels.ServerSocketChannel}&nbsp;&nbsp;</tt></td>
<td>A channel for a {@link java.net.ServerSocket java.net.ServerSocket}</td></tr>
<tr><td valign=top><tt>&nbsp;&nbsp;{@link java.nio.channels.SocketChannel}</tt></td>
<td>A channel for a {@link java.net.Socket java.net.Socket}</td></tr>
<tr><td valign=top><tt>{@link java.nio.channels.Selector}</tt></td>
<td>A multiplexor of selectable channels</td></tr>
<tr><td valign=top><tt>{@link java.nio.channels.SelectionKey}</tt></td>
<td>A token representing the registration <br> of a channel
with&nbsp;a&nbsp;selector</td></tr>
<tr><td valign=top><tt>{@link java.nio.channels.Pipe}</tt></td>
<td>Two channels that form a unidirectional&nbsp;pipe</td></tr>
</table></blockquote>
<p> Multiplexed, non-blocking I/O, which is much more scalable than
thread-oriented, blocking I/O, is provided by <i>selectors</i>, <i>selectable
channels</i>, and <i>selection keys</i>.
<p> A <a href="Selector.html"><i>selector</i></a> is a multiplexor of <a
href="SelectableChannel.html"><i>selectable channels</i></a>, which in turn are
a special type of channel that can be put into <a
href="SelectableChannel.html#bm"><i>non-blocking mode</i></a>. To perform
multiplexed I/O operations, one or more selectable channels are first created,
put into non-blocking mode, and {@link
java.nio.channels.SelectableChannel#register </code><i>registered</i><code>}
with a selector. Registering a channel specifies the set of I/O operations
that will be tested for readiness by the selector, and returns a <a
href="SelectionKey.html"><i>selection key</i></a> that represents the
registration.
<p> Once some channels have been registered with a selector, a <a
href="Selector.html#selop"><i>selection operation</i></a> can be performed in
order to discover which channels, if any, have become ready to perform one or
more of the operations in which interest was previously declared. If a channel
is ready then the key returned when it was registered will be added to the
selector's <i>selected-key set</i>. The key set, and the keys within it, can
be examined in order to determine the operations for which each channel is
ready. From each key one can retrieve the corresponding channel in order to
perform whatever I/O operations are required.
<p> That a selection key indicates that its channel is ready for some operation
is a hint, but not a guarantee, that such an operation can be performed by a
thread without causing the thread to block. It is imperative that code that
performs multiplexed I/O be written so as to ignore these hints when they prove
to be incorrect.
<p> This package defines selectable-channel classes corresponding to the {@link
java.net.DatagramSocket}, {@link java.net.ServerSocket}, and {@link
java.net.Socket} classes defined in the <tt>{@link java.net}</tt> package.
Minor changes to these classes have been made in order to support sockets that
are associated with channels. This package also defines a simple class that
implements unidirectional pipes. In all cases, a new selectable channel is
created by invoking the static <tt>open</tt> method of the corresponding class.
If a channel needs an associated socket then a socket will be created as a side
effect of this operation.
<p> The implementation of selectors, selectable channels, and selection keys
can be replaced by "plugging in" an alternative definition or instance of the
{@link java.nio.channels.spi.SelectorProvider} class defined in the <tt>{@link
java.nio.channels.spi}</tt> package. It is not expected that many developers
will actually make use of this facility; it is provided primarily so that
sophisticated users can take advantage of operating-system-specific
I/O-multiplexing mechanisms when very high performance is required.
<p> Much of the bookkeeping and synchronization required to implement the
multiplexed-I/O abstractions is performed by the {@link
java.nio.channels.spi.AbstractInterruptibleChannel}, {@link
java.nio.channels.spi.AbstractSelectableChannel}, {@link
java.nio.channels.spi.AbstractSelectionKey}, and {@link
java.nio.channels.spi.AbstractSelector} classes in the <tt>{@link
java.nio.channels.spi}</tt> package. When defining a custom selector provider,
only the {@link java.nio.channels.spi.AbstractSelector} and {@link
java.nio.channels.spi.AbstractSelectionKey} classes should be subclassed
directly; custom channel classes should extend the appropriate {@link
java.nio.channels.SelectableChannel} subclasses defined in this package.
<p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
or method in any class or interface in this package will cause a {@link
java.lang.NullPointerException NullPointerException} to be thrown.
@since 1.4
@author Mark Reinhold
@author JSR-51 Expert Group
</body>
</html>

View file

@ -25,10 +25,8 @@
package java.nio.channels.spi;
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.ProtocolFamily;
import java.nio.channels.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
@ -189,6 +187,24 @@ public abstract class SelectorProvider {
public abstract DatagramChannel openDatagramChannel()
throws IOException;
/**
* Opens a datagram channel.
*
* @param family
* The protocol family
*
* @return A new datagram channel
*
* @throws UnsupportedOperationException
* If the specified protocol family is not supported
* @throws IOException
* If an I/O error occurs
*
* @since 1.7
*/
public abstract DatagramChannel openDatagramChannel(ProtocolFamily family)
throws IOException;
/**
* Opens a pipe. </p>
*

View file

@ -51,4 +51,16 @@ public class InstanceNotFoundException extends OperationsException {
public InstanceNotFoundException(String message) {
super(message);
}
/**
* Constructor for the frequent case where the message is the ObjectName
* of the missing MBean.
*
* @param name the ObjectName of the missing MBean.
*
* @since 1.7
*/
public InstanceNotFoundException(ObjectName name) {
this(name.toString());
}
}

View file

@ -25,6 +25,7 @@
package javax.management;
import com.sun.jmx.mbeanserver.Util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.security.Permission;
@ -42,10 +43,10 @@ import java.security.Permission;
* permission that you <em>need</em>. When a sensitive operation is
* being checked for permission, an MBeanPermission is constructed
* representing the permission you need. The operation is only
* allowed if the permissions you have {@link #implies imply} the
* allowed if the permissions you have {@linkplain #implies imply} the
* permission you need.</p>
*
* <p>An MBeanPermission contains four items of information:</p>
* <p>An MBeanPermission contains five items of information:</p>
*
* <ul>
*
@ -57,6 +58,23 @@ import java.security.Permission;
*
* <p>The action is returned by {@link #getActions()}.</p>
*
* <li id="MBeanServerName"><p>The <em>MBean Server name</em>.</p>
*
* <p>For a permission you need, this is the {@linkplain
* javax.management.MBeanServerFactory#getMBeanServerName
* name of the MBeanServer}
* containing the <a href="#MBeanName">MBean</a> for which the MBean
* permission is checked.</p>
*
* <p>For a permission you have, this is either the {@linkplain
* javax.management.MBeanServerFactory#getMBeanServerName
* name of the MBeanServer} in which the <a href="#MBeanName">MBean</a>
* you have this permission for must be registered,
* or a pattern against which that MBean Server name will be matched.<br>
* An {@code mbeanServerName} pattern can also be empty or the single
* character {@code "*"}, both of which will match any {@code MBeanServer} name.
* </p>
*
* <li><p>The <em>class name</em>.</p>
*
* <p>For a permission you need, this is the class name of an MBean
@ -88,7 +106,7 @@ import java.security.Permission;
* or operation you can access, or it is empty or the single character
* "<code>*</code>", both of which grant access to any member.</p>
*
* <li><p>The <em>object name</em>.</p>
* <li id="MBeanName"><p>The <em>object name</em>.</p>
*
* <p>For a permission you need, this is the {@link ObjectName} of the
* MBean you are accessing. For operations that do not reference a
@ -103,15 +121,15 @@ import java.security.Permission;
* </ul>
*
* <p>If you have an MBeanPermission, it allows operations only if all
* four of the items match.</p>
* five of the items match.</p>
*
* <p>The class name, member, and object name can be written together
* as a single string, which is the <em>name</em> of this permission.
* <p>The MBean Server name, class name, member, and object name can be written
* together as a single string, which is the <em>name</em> of this permission.
* The name of the permission is the string returned by {@link
* Permission#getName() getName()}. The format of the string is:</p>
*
* <blockquote>
* <code>className#member[objectName]</code>
* <code>mbeanServerName::className#member[objectName]</code>
* </blockquote>
*
* <p>The object name is written using the usual syntax for {@link
@ -119,15 +137,18 @@ import java.security.Permission;
* <code>]</code>. It is terminated by a <code>]</code> character
* that is the last character in the string.</p>
*
* <p>One or more of the <code>className</code>, <code>member</code>,
* or <code>objectName</code> may be omitted. If the
* <code>member</code> is omitted, the <code>#</code> may be too (but
* <p>One or more of the <code>mbeanServerName</code>, <code>className</code>,
* <code>member</code>, or <code>objectName</code> may be omitted. If the
* <code>mbeanServerName</code> is omitted, the <code>::</code> may be too (but
* does not have to be).
* If the <code>member</code> is omitted, the <code>#</code> may be too (but
* does not have to be). If the <code>objectName</code> is omitted,
* the <code>[]</code> may be too (but does not have to be). It is
* not legal to omit all three items, that is to have a <em>name</em>
* not legal to omit all four items, that is to have a <em>name</em>
* that is the empty string.</p>
*
* <p>One or more of the <code>className</code>, <code>member</code>,
* <p>One or more of the <code>mbeanServerName</code>, <code>className</code>,
* <code>member</code>,
* or <code>objectName</code> may be the character "<code>-</code>",
* which is equivalent to a null value. A null value is implied by
* any value (including another null value) but does not imply any
@ -246,6 +267,13 @@ public class MBeanPermission extends Permission {
*/
private transient ObjectName objectName;
/**
* The name of the MBeanServer in which this permission is checked, or
* granted. If null, is implied by any MBean Server name
* but does not imply any non-null MBean Server name.
*/
private transient String mbeanServerName;
/**
* Parse <code>actions</code> parameter.
*/
@ -283,6 +311,13 @@ public class MBeanPermission extends Permission {
throw new IllegalArgumentException("MBeanPermission name " +
"cannot be empty");
final int sepIndex = name.indexOf("::");
if (sepIndex < 0) {
setMBeanServerName("*");
} else {
setMBeanServerName(name.substring(0,sepIndex));
}
/* The name looks like "class#member[objectname]". We subtract
elements from the right as we parse, so after parsing the
objectname we have "class#member" and after parsing the
@ -290,11 +325,14 @@ public class MBeanPermission extends Permission {
// Parse ObjectName
int openingBracket = name.indexOf("[");
final int start = (sepIndex<0)?0:sepIndex+2;
int openingBracket = name.indexOf("[",start);
if (openingBracket == -1) {
// If "[on]" missing then ObjectName("*:*")
//
objectName = ObjectName.WILDCARD;
name = name.substring(start);
} else {
if (!name.endsWith("]")) {
throw new IllegalArgumentException("MBeanPermission: " +
@ -305,11 +343,11 @@ public class MBeanPermission extends Permission {
} else {
// Create ObjectName
//
String on = name.substring(openingBracket + 1,
name.length() - 1);
try {
// If "[]" then ObjectName("*:*")
//
String on = name.substring(openingBracket + 1,
name.length() - 1);
if (on.equals(""))
objectName = ObjectName.WILDCARD;
else if (on.equals("-"))
@ -320,11 +358,11 @@ public class MBeanPermission extends Permission {
throw new IllegalArgumentException("MBeanPermission: " +
"The target name does " +
"not specify a valid " +
"ObjectName");
"ObjectName", e);
}
}
name = name.substring(0, openingBracket);
name = name.substring(start, openingBracket);
}
// Parse member
@ -348,8 +386,9 @@ public class MBeanPermission extends Permission {
* Assign fields based on className, member, and objectName
* parameters.
*/
private void initName(String className, String member,
ObjectName objectName) {
private void initName(String mbeanServerName, String className,
String member, ObjectName objectName) {
setMBeanServerName(mbeanServerName);
setClassName(className);
setMember(member);
this.objectName = objectName;
@ -381,19 +420,30 @@ public class MBeanPermission extends Permission {
this.member = member;
}
private void setMBeanServerName(String mbeanServerName) {
if (mbeanServerName == null || mbeanServerName.equals("-")) {
this.mbeanServerName = null;
} else if (mbeanServerName.equals("")) {
this.mbeanServerName = "*";
} else {
this.mbeanServerName = mbeanServerName;
}
}
/**
* <p>Create a new MBeanPermission object with the specified target name
* and actions.</p>
*
* <p>The target name is of the form
* "<code>className#member[objectName]</code>" where each part is
* optional. It must not be empty or null.</p>
* "<code>mbeanServerName::className#member[objectName]</code>" where
* each part is optional. It must not be empty or null.</p>
*
* <p>The actions parameter contains a comma-separated list of the
* desired actions granted on the target name. It must not be
* empty or null.</p>
*
* @param name the triplet "className#member[objectName]".
* @param name the quadruplet "mbeanServerName::className#member[objectName]".
* @param actions the action string.
*
* @exception IllegalArgumentException if the <code>name</code> or
@ -418,6 +468,12 @@ public class MBeanPermission extends Permission {
* optional. This will be the result of {@link #getName()} on the
* resultant MBeanPermission.</p>
*
* <p>This corresponds to a permission granted for all
* MBean servers present in the JVM and is equivalent to
* {@link #MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission("*",className,member,objectName,actions)}.
* </p>
*
* <p>The actions parameter contains a comma-separated list of the
* desired actions granted on the target name. It must not be
* empty or null.</p>
@ -439,17 +495,67 @@ public class MBeanPermission extends Permission {
String member,
ObjectName objectName,
String actions) {
this("*",className,member,objectName,actions);
}
super(makeName(className, member, objectName));
initName(className, member, objectName);
/**
* <p>Create a new MBeanPermission object with the specified target name
* (MBean Server name, class name, member, object name) and actions.</p>
*
* <p>The MBean Server name, class name, member and object name
* parameters define a target name of the form
* "<code>mbeanServerName::className#member[objectName]</code>" where each
* part is optional. This will be the result of {@link #getName()} on the
* resultant MBeanPermission.
* If the <code>mbeanServerName</code> is empty or exactly {@code "*"}, then
* "{@code mbeanServerName::}" is omitted in that result.
* </p>
*
* <p>The actions parameter contains a comma-separated list of the
* desired actions granted on the target name. It must not be
* empty or null.</p>
*
* @param mbeanServerName the name of the {@code MBeanServer} to which this
* permission applies.
* May be null or <code>"-"</code>, which represents an MBeanServer name
* that is implied by any MBeanServer name but does not imply any other
* MBeanServer name.
* @param className the class name to which this permission applies.
* May be null or <code>"-"</code>, which represents a class name
* that is implied by any class name but does not imply any other
* class name.
* @param member the member to which this permission applies. May
* be null or <code>"-"</code>, which represents a member that is
* implied by any member but does not imply any other member.
* @param objectName the object name to which this permission
* applies. May be null, which represents an object name that is
* implied by any object name but does not imply any other object
* name.
* @param actions the action string.
*
* @since 1.7
*/
public MBeanPermission(String mbeanServerName,
String className,
String member,
ObjectName objectName,
String actions) {
super(makeName(mbeanServerName,className, member, objectName));
initName(mbeanServerName,className, member, objectName);
this.actions = actions;
parseActions();
}
private static String makeName(String className, String member,
private static String makeName(String mbeanServerName, String className,
String member,
ObjectName objectName) {
final StringBuilder name = new StringBuilder();
if (mbeanServerName == null)
mbeanServerName = "-";
if (!mbeanServerName.equals("") && !mbeanServerName.equals("*"))
name.append(mbeanServerName).append("::");
if (className == null)
className = "-";
name.append(className);
@ -991,6 +1097,9 @@ public class MBeanPermission extends Permission {
*
* <li> <i>p</i> is an instance of MBeanPermission; and</li>
*
* <li> <i>p</i> has a null mbeanServerName or <i>p</i>'s mbeanServerName
* matches this object's mbeanServerName; and</li>
*
* <li> <i>p</i> has a null className or <i>p</i>'s className
* matches this object's className; and</li>
*
@ -1004,6 +1113,13 @@ public class MBeanPermission extends Permission {
*
* </ul>
*
* <p>If this object's mbeanServerName is a pattern, then <i>p</i>'s
* mbeanServerName is matched against that pattern. An empty
* mbeanServerName is equivalent to "{@code *}". A null
* mbeanServerName is equivalent to "{@code -}".</p>
* <p>If this object's mbeanServerName is "<code>*</code>" or is
* empty, <i>p</i>'s mbeanServerName always matches it.</p>
*
* <p>If this object's className is "<code>*</code>", <i>p</i>'s
* className always matches it. If it is "<code>a.*</code>", <i>p</i>'s
* className matches it if it begins with "<code>a.</code>".</p>
@ -1050,6 +1166,12 @@ public class MBeanPermission extends Permission {
// Target name
//
// The 'mbeanServerName' check is true iff:
// 1) the mbeanServerName in 'this' permission is omitted or "*", or
// 2) the mbeanServerName in 'that' permission is omitted or "*", or
// 3) the mbeanServerName in 'this' permission does pattern
// matching with the mbeanServerName in 'that' permission.
//
// The 'className' check is true iff:
// 1) the className in 'this' permission is omitted or "*", or
// 2) the className in 'that' permission is omitted or "*", or
@ -1076,6 +1198,17 @@ public class MBeanPermission extends Permission {
expect that "that" contains a wildcard, since it is a
needed permission. So we assume that.classNameExactMatch. */
if (that.mbeanServerName == null) {
// bottom is implied
} else if (this.mbeanServerName == null) {
// bottom implies nothing but itself
return false;
} else if (that.mbeanServerName.equals(this.mbeanServerName)) {
// exact match
} else if (!Util.wildmatch(that.mbeanServerName,this.mbeanServerName)) {
return false; // no match
}
if (that.classNamePrefix == null) {
// bottom is implied
} else if (this.classNamePrefix == null) {

View file

@ -42,7 +42,7 @@ import javax.management.loading.ClassLoaderRepository;
*
* <p>User code does not usually implement this interface. Instead,
* an object that implements this interface is obtained with one of
* the methods in the {@link MBeanServerFactory} class.</p>
* the methods in the {@link javax.management.MBeanServerFactory} class.</p>
*
* <p>Every MBean which is added to the MBean server becomes
* manageable: its attributes and operations become remotely
@ -62,8 +62,12 @@ import javax.management.loading.ClassLoaderRepository;
* <CODE>JMImplementation:type=MBeanServerDelegate</CODE>.</p>
*
* <p id="security">An object obtained from the {@link
* MBeanServerFactory#createMBeanServer(String) createMBeanServer} or
* {@link MBeanServerFactory#newMBeanServer(String) newMBeanServer}
* MBeanServerFactory#createMBeanServer(String) createMBeanServer}, {@link
* MBeanServerFactory#createNamedMBeanServer(String,String) createNamedMBeanServer},
* {@link
* MBeanServerFactory#newMBeanServer(String) newMBeanServer}, or
* {@link
* MBeanServerFactory#newNamedMBeanServer(String,String) newNamedMBeanServer}
* methods of the {@link MBeanServerFactory} class applies security
* checks to its methods, as follows.</p>
*
@ -73,9 +77,26 @@ import javax.management.loading.ClassLoaderRepository;
*
* <p>Assuming that there is a security manager, or that the
* implementation chooses to make checks anyway, the checks are made
* as detailed below. In what follows, <code>className</code> is the
* as detailed below.
* In what follows, and unless otherwise specified:
* </p>
* <ul><li><code>className</code> is the
* string returned by {@link MBeanInfo#getClassName()} for the target
* MBean.</p>
* MBean,</li>
* <li>{@code mbeanServerName} is the
* {@linkplain MBeanServerFactory#getMBeanServerName name of the
* MBean Server} in which the target MBean is registered. This is the
* value returned by {@link MBeanServerFactory#getMBeanServerName
* MBeanServerFactory.getMBeanServerName(MBeanServer)}, and
* is usually the {@code mbeanServerName} parameter that was supplied
* to the {@link
* MBeanServerFactory#createNamedMBeanServer(String,String)
* createNamedMBeanServer} or {@link
* MBeanServerFactory#newNamedMBeanServer(String,String) newNamedMBeanServer}
* methods of the {@link MBeanServerFactory} when the MBeanServer was created,
* or {@value javax.management.MBeanServerFactory#DEFAULT_MBEANSERVER_NAME} if
* no name was supplied.
* </li></ul>
*
* <p>If a security check fails, the method throws {@link
* SecurityException}.</p>
@ -89,78 +110,86 @@ import javax.management.loading.ClassLoaderRepository;
*
* <li><p>For the {@link #invoke invoke} method, the caller's
* permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, operationName, name, "invoke")}.</p>
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, operationName, name, "invoke")}.
* </p>
*
* <li><p>For the {@link #getAttribute getAttribute} method, the
* caller's permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, attribute, name, "getAttribute")}.</p>
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, attribute, name,
* "getAttribute")}.</p>
*
* <li><p>For the {@link #getAttributes getAttributes} method, the
* caller's permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, null, name, "getAttribute")}.
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName,className, null, name, "getAttribute")}.
* Additionally, for each attribute <em>a</em> in the {@link
* AttributeList}, if the caller's permissions do not imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, <em>a</em>, name, "getAttribute")}, the
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, <em>a</em>, name,
* "getAttribute")}, the
* MBean server will behave as if that attribute had not been in the
* supplied list.</p>
*
* <li><p>For the {@link #setAttribute setAttribute} method, the
* caller's permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, attrName, name, "setAttribute")}, where
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, attrName, name,
* "setAttribute")}, where
* <code>attrName</code> is {@link Attribute#getName()
* attribute.getName()}.</p>
*
* <li><p>For the {@link #setAttributes setAttributes} method, the
* caller's permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, null, name, "setAttribute")}.
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, null, name, "setAttribute")}.
* Additionally, for each attribute <em>a</em> in the {@link
* AttributeList}, if the caller's permissions do not imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, <em>a</em>, name, "setAttribute")}, the
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, <em>a</em>, name,
* "setAttribute")}, the
* MBean server will behave as if that attribute had not been in the
* supplied list.</p>
*
* <li><p>For the <code>addNotificationListener</code> methods,
* the caller's permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, null, name,
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, null, name,
* "addNotificationListener")}.</p>
*
* <li><p>For the <code>removeNotificationListener</code> methods,
* the caller's permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, null, name,
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, null, name,
* "removeNotificationListener")}.</p>
*
* <li><p>For the {@link #getMBeanInfo getMBeanInfo} method, the
* caller's permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, null, name, "getMBeanInfo")}.</p>
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, null, name, "getMBeanInfo")}.
* </p>
*
* <li><p>For the {@link #getObjectInstance getObjectInstance} method,
* the caller's permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, null, name, "getObjectInstance")}.</p>
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, null, name,
* "getObjectInstance")}.</p>
*
* <li><p>For the {@link #isInstanceOf isInstanceOf} method, the
* caller's permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, null, name, "isInstanceOf")}.</p>
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, null, name, "isInstanceOf")}.
* </p>
*
* <li><p>For the {@link #queryMBeans queryMBeans} method, the
* caller's permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(null, null, name, "queryMBeans")}.
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, null, null, null, "queryMBeans")}.
* Additionally, for each MBean that matches <code>name</code>,
* if the caller's permissions do not imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, null, name, "queryMBeans")}, the
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, null, name, "queryMBeans")}, the
* MBean server will behave as if that MBean did not exist.</p>
*
* <p>Certain query elements perform operations on the MBean server.
@ -179,10 +208,10 @@ import javax.management.loading.ClassLoaderRepository;
*
* <li><p>For the {@link #getDomains getDomains} method, the caller's
* permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(null, null, name, "getDomains")}. Additionally,
* for each domain <var>d</var> in the returned array, if the caller's
* permissions do not imply {@link
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, null, null, null, "getDomains")}.
* Additionally, for each domain <var>d</var> in the returned array, if the
* caller's permissions do not imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(null, null, new ObjectName("<var>d</var>:x=x"),
* "getDomains")}, the domain is eliminated from the array. Here,
@ -191,21 +220,22 @@ import javax.management.loading.ClassLoaderRepository;
*
* <li><p>For the {@link #getClassLoader getClassLoader} method, the
* caller's permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, null, loaderName,
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, null, loaderName,
* "getClassLoader")}.</p>
*
* <li><p>For the {@link #getClassLoaderFor getClassLoaderFor} method,
* the caller's permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, null, mbeanName,
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, null, mbeanName,
* "getClassLoaderFor")}.</p>
*
* <li><p>For the {@link #getClassLoaderRepository
* getClassLoaderRepository} method, the caller's permissions must
* imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(null, null, null, "getClassLoaderRepository")}.</p>
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, null, null, null,
* "getClassLoaderRepository")}.</p>
*
* <li><p>For the deprecated <code>deserialize</code> methods, the
* required permissions are the same as for the methods that replace
@ -213,15 +243,15 @@ import javax.management.loading.ClassLoaderRepository;
*
* <li><p>For the <code>instantiate</code> methods, the caller's
* permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, null, null, "instantiate")}.</p>
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, null, null, "instantiate")},
* where {@code className} is the name of the class which is to
* be instantiated.</p>
*
* <li><p>For the {@link #registerMBean registerMBean} method, the
* caller's permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, null, name, "registerMBean")}. Here
* <code>className</code> is the string returned by {@link
* MBeanInfo#getClassName()} for an object of this class.
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, null, name, "registerMBean")}.
*
* <p>If the <code>MBeanPermission</code> check succeeds, the MBean's
* class is validated by checking that its {@link
@ -241,8 +271,9 @@ import javax.management.loading.ClassLoaderRepository;
*
* <li><p>For the {@link #unregisterMBean unregisterMBean} method,
* the caller's permissions must imply {@link
* MBeanPermission#MBeanPermission(String,String,ObjectName,String)
* MBeanPermission(className, null, name, "unregisterMBean")}.</p>
* MBeanPermission#MBeanPermission(String,String,String,ObjectName,String)
* MBeanPermission(mbeanServerName, className, null, name, "unregisterMBean")}.
* </p>
*
* </ul>
*
@ -264,6 +295,8 @@ public interface MBeanServer extends MBeanServerConnection {
* is sent as described <a href="#notif">above</a>.</p>
*
* @throws RuntimeOperationsException {@inheritDoc}
* @throws RuntimeMBeanException {@inheritDoc}
* @throws RuntimeErrorException {@inheritDoc}
*/
public ObjectInstance createMBean(String className, ObjectName name)
throws ReflectionException, InstanceAlreadyExistsException,
@ -276,6 +309,8 @@ public interface MBeanServer extends MBeanServerConnection {
* is sent as described <a href="#notif">above</a>.</p>
*
* @throws RuntimeOperationsException {@inheritDoc}
* @throws RuntimeMBeanException {@inheritDoc}
* @throws RuntimeErrorException {@inheritDoc}
*/
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName)
@ -289,6 +324,8 @@ public interface MBeanServer extends MBeanServerConnection {
* is sent as described <a href="#notif">above</a>.</p>
*
* @throws RuntimeOperationsException {@inheritDoc}
* @throws RuntimeMBeanException {@inheritDoc}
* @throws RuntimeErrorException {@inheritDoc}
*/
public ObjectInstance createMBean(String className, ObjectName name,
Object params[], String signature[])
@ -302,6 +339,8 @@ public interface MBeanServer extends MBeanServerConnection {
* is sent as described <a href="#notif">above</a>.</p>
*
* @throws RuntimeOperationsException {@inheritDoc}
* @throws RuntimeMBeanException {@inheritDoc}
* @throws RuntimeErrorException {@inheritDoc}
*/
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName, Object params[],

View file

@ -78,19 +78,19 @@ public interface MBeanServerConnection extends NotificationManager {
* MBean will not be registered.
* @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
* <CODE>RuntimeException</CODE>, the <CODE>createMBean<CODE> method will
* <CODE>RuntimeException</CODE>, the <CODE>createMBean</CODE> method will
* throw a <CODE>RuntimeMBeanException</CODE>, although the MBean creation
* and registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>createMBean<CODE> method
* registered even though the <CODE>createMBean</CODE> method
* threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
* @exception RuntimeErrorException If the <CODE>postRegister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
* <CODE>Error</CODE>, the <CODE>createMBean<CODE> method will
* <CODE>Error</CODE>, the <CODE>createMBean</CODE> method will
* throw a <CODE>RuntimeErrorException</CODE>, although the MBean creation
* and registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>createMBean<CODE> method
* registered even though the <CODE>createMBean</CODE> method
* threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
@ -150,19 +150,19 @@ public interface MBeanServerConnection extends NotificationManager {
* MBean will not be registered.
* @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
* <CODE>RuntimeException</CODE>, the <CODE>createMBean<CODE> method will
* <CODE>RuntimeException</CODE>, the <CODE>createMBean</CODE> method will
* throw a <CODE>RuntimeMBeanException</CODE>, although the MBean creation
* and registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>createMBean<CODE> method
* registered even though the <CODE>createMBean</CODE> method
* threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
* @exception RuntimeErrorException If the <CODE>postRegister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
* <CODE>Error</CODE>, the <CODE>createMBean<CODE> method will
* <CODE>Error</CODE>, the <CODE>createMBean</CODE> method will
* throw a <CODE>RuntimeErrorException</CODE>, although the MBean creation
* and registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>createMBean<CODE> method
* registered even though the <CODE>createMBean</CODE> method
* threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
@ -225,19 +225,19 @@ public interface MBeanServerConnection extends NotificationManager {
* MBean will not be registered.
* @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
* <CODE>RuntimeException</CODE>, the <CODE>createMBean<CODE> method will
* <CODE>RuntimeException</CODE>, the <CODE>createMBean</CODE> method will
* throw a <CODE>RuntimeMBeanException</CODE>, although the MBean creation
* and registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>createMBean<CODE> method
* registered even though the <CODE>createMBean</CODE> method
* threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
* @exception RuntimeErrorException If the <CODE>postRegister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
* <CODE>Error</CODE>, the <CODE>createMBean<CODE> method will
* <CODE>Error</CODE>, the <CODE>createMBean</CODE> method will
* throw a <CODE>RuntimeErrorException</CODE>, although the MBean creation
* and registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>createMBean<CODE> method
* registered even though the <CODE>createMBean</CODE> method
* threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
@ -297,19 +297,19 @@ public interface MBeanServerConnection extends NotificationManager {
* MBean will not be registered.
* @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
* <CODE>RuntimeException</CODE>, the <CODE>createMBean<CODE> method will
* <CODE>RuntimeException</CODE>, the <CODE>createMBean</CODE> method will
* throw a <CODE>RuntimeMBeanException</CODE>, although the MBean creation
* and registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>createMBean<CODE> method
* registered even though the <CODE>createMBean</CODE> method
* threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
* @exception RuntimeErrorException If the <CODE>postRegister</CODE> method
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
* <CODE>Error</CODE>, the <CODE>createMBean<CODE> method will
* <CODE>Error</CODE>, the <CODE>createMBean</CODE> method will
* throw a <CODE>RuntimeErrorException</CODE>, although the MBean creation
* and registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>createMBean<CODE> method
* registered even though the <CODE>createMBean</CODE> method
* threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
@ -351,19 +351,19 @@ public interface MBeanServerConnection extends NotificationManager {
* has thrown an exception.
* @exception RuntimeMBeanException If the <CODE>postDeregister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
* <CODE>RuntimeException</CODE>, the <CODE>unregisterMBean<CODE> method
* <CODE>RuntimeException</CODE>, the <CODE>unregisterMBean</CODE> method
* will throw a <CODE>RuntimeMBeanException</CODE>, although the MBean
* unregistration succeeded. In such a case, the MBean will be actually
* unregistered even though the <CODE>unregisterMBean<CODE> method
* unregistered even though the <CODE>unregisterMBean</CODE> method
* threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
* also be thrown by <CODE>preDeregister</CODE>, in which case the MBean
* will remain registered.
* @exception RuntimeErrorException If the <CODE>postDeregister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
* <CODE>Error</CODE>, the <CODE>unregisterMBean<CODE> method will
* <CODE>Error</CODE>, the <CODE>unregisterMBean</CODE> method will
* throw a <CODE>RuntimeErrorException</CODE>, although the MBean
* unregistration succeeded. In such a case, the MBean will be actually
* unregistered even though the <CODE>unregisterMBean<CODE> method
* unregistered even though the <CODE>unregisterMBean</CODE> method
* threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
* also be thrown by <CODE>preDeregister</CODE>, in which case the MBean
* will remain registered.

View file

@ -25,7 +25,9 @@
package javax.management;
import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.defaults.ServiceName;
import com.sun.jmx.mbeanserver.Util;
/**
* Represents the MBean server from the management point of view.
@ -39,6 +41,7 @@ public class MBeanServerDelegate implements MBeanServerDelegateMBean,
/** The MBean server agent identification.*/
private String mbeanServerId ;
private String mbeanServerName;
/** The NotificationBroadcasterSupport object that sends the
notifications */
@ -68,6 +71,7 @@ public class MBeanServerDelegate implements MBeanServerDelegateMBean,
public MBeanServerDelegate () {
stamp = getStamp();
broadcaster = new NotificationBroadcasterSupport() ;
mbeanServerName=null;
}
@ -82,13 +86,102 @@ public class MBeanServerDelegate implements MBeanServerDelegateMBean,
try {
localHost = java.net.InetAddress.getLocalHost().getHostName();
} catch (java.net.UnknownHostException e) {
JmxProperties.MISC_LOGGER.finest("Can't get local host name, " +
"using \"localhost\" instead. Cause is: "+e);
localHost = "localhost";
}
mbeanServerId = localHost + "_" + stamp;
mbeanServerId =
Util.insertMBeanServerName(localHost + "_" + stamp,
mbeanServerName);
}
return mbeanServerId;
}
/**
* The name of the MBeanServer.
* @return The name of the MBeanServer, or {@value
* javax.management.MBeanServerFactory#DEFAULT_MBEANSERVER_NAME} if no
* name was specified.
*
* @since 1.7
* @see #setMBeanServerName
*/
public synchronized String getMBeanServerName() {
if (Util.isMBeanServerNameUndefined(mbeanServerName))
return MBeanServerFactory.DEFAULT_MBEANSERVER_NAME;
return mbeanServerName;
}
/**
* Sets the name of the MBeanServer. The name will be embedded into the
* {@link #getMBeanServerId MBeanServerId} using the following format:<br>
* {@code mbeanServerId: <mbeanServerId>;mbeanServerName=<mbeanServerName>}
* <p>The characters {@code ':'} (colon), {@code ';'} (semicolon ),
* {@code '*'} (star) and {@code '?'} (question mark) are not legal in an
* MBean Server name.</p>
* <p>For instance, if the {@code mbeanServerName} provided is
* {@code "com.mycompany.myapp.server1"}, and the original
* {@code MBeanServerId} was {@code "myhost_1213353064145"},
* then {@code mbeanServerName} will be
* embedded in the {@code MBeanServerId} - and the new value of the
* {@code MBeanServerId} will be:
* </p>
* <pre>
* "myhost_1213353064145;mbeanServerName=com.mycompany.myapp.server1"
* </pre>
* <p>Note: The {@code mbeanServerName} is usually set by the
* {@code MBeanServerFactory}. It is set only once, before the
* MBean Server is returned by the factory. Once the MBean Server name is
* set, it is not possible to change it.
* </p>
* @param mbeanServerName The MBeanServer name.
* @throws IllegalArgumentException if the MBeanServerName is already set
* to a different value, or if the provided name contains
* illegal characters, or if the provided name is {@code ""}
* (the empty string) or "-" (dash).
* @throws UnsupportedOperationException if this object is of a legacy
* subclass of MBeanServerDelegate which overrides {@link
* #getMBeanServerId()}
* in a way that doesn't support setting an MBeanServer name.
* @see MBeanServerFactory#getMBeanServerName
* @since 1.7
*/
public synchronized void setMBeanServerName(String mbeanServerName) {
// Sets the name on the delegate. For complex backward
// compatibility reasons it is not possible to give the
// name to the MBeanServerDelegate constructor.
//
// The method setMBeanServerName() will call getMBeanServerId()
// to check that the name is accurately set in the MBeanServerId.
// If not (which could happen if a custom MBeanServerDelegate
// implementation overrides getMBeanServerId() and was not updated
// with respect to JMX 2.0 spec), this method will throw an
// IllegalStateException...
// will fail if mbeanServerName is illegal
final String name = Util.checkServerName(mbeanServerName);
// can only set mbeanServerDelegate once.
if (this.mbeanServerName != null && !this.mbeanServerName.equals(name))
throw new IllegalArgumentException(
"MBeanServerName already set to a different value");
this.mbeanServerName = name;
// will fail if mbeanServerId already has a different mbeanServerName
mbeanServerId =
Util.insertMBeanServerName(getMBeanServerId(),name);
// check that we don't have a subclass which overrides
// getMBeanServerId() without setting mbeanServerName
if (!name.equals(
Util.extractMBeanServerName(getMBeanServerId())))
throw new UnsupportedOperationException(
"Can't set MBeanServerName in MBeanServerId - " +
"unsupported by "+this.getClass().getName()+"?");
// OK: at this point we know that we have correctly set mbeanServerName.
}
/**
* Returns the full name of the JMX specification implemented
* by this product.
@ -210,15 +303,8 @@ public class MBeanServerDelegate implements MBeanServerDelegateMBean,
*
* @since 1.6
*/
public static final ObjectName DELEGATE_NAME;
static {
try {
DELEGATE_NAME =
new ObjectName("JMImplementation:type=MBeanServerDelegate");
} catch (MalformedObjectNameException e) {
throw new Error("Can't initialize delegate name", e);
}
}
public static final ObjectName DELEGATE_NAME =
Util.newObjectName("JMImplementation:type=MBeanServerDelegate");
/* Return a timestamp that is monotonically increasing even if
System.currentTimeMillis() isn't (for example, if you call this

View file

@ -25,16 +25,19 @@
package javax.management;
import com.sun.jmx.defaults.JmxProperties;
import static com.sun.jmx.defaults.JmxProperties.JMX_INITIAL_BUILDER;
import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
import com.sun.jmx.interceptor.DefaultMBeanServerInterceptor;
import com.sun.jmx.mbeanserver.GetPropertyAction;
import com.sun.jmx.mbeanserver.Util;
import java.security.AccessController;
import java.security.Permission;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import javax.management.loading.ClassLoaderRepository;
/**
* <p>Provides MBean server references. There are no instances of
* this class.</p>
@ -80,10 +83,53 @@ import javax.management.loading.ClassLoaderRepository;
* returned by the default MBeanServerBuilder implementation, for the purpose
* of e.g. adding an additional security layer.</p>
*
* <p id="MBeanServerName">Since version 2.0 of the JMX API, when creating
* an MBeanServer,
* it is possible to specify an {@linkplain #getMBeanServerName
* MBean Server name}.
* To create an MBean Server with a name, the MBeanServerFactory provides two
* new methods:</p>
* <ul><li>{@link #createNamedMBeanServer
* createNamedMBeanServer(mbeanServerName, defaultDomain)}: creates a named
* MBeanServer and keeps an internal reference to the created object. The
* MBeanServer can be later retrieved using {@link #findMBeanServer
* findMBeanServer(mbeanServerId)} or
* {@link #findMBeanServerByName findMBeanServerByName(mbeanServerName)}, and
* can be released through {@link
* #releaseMBeanServer releaseMBeanServer(mbeanServer)}.</li>
* <li>{@link #newNamedMBeanServer
* newNamedMBeanServer(mbeanServerName, defaultDomain)}:
* creates a named MBeanServer without keeping any internal reference to the
* named server.</li>
* </ul>
* <p>The name of the MBeanServer is stored in the
* {@linkplain MBeanServerDelegate MBean Server delegate MBean}
* and is embedded in its {@link MBeanServerDelegate#getMBeanServerId
* MBeanServerId} attribute.</p>
* <p>The name of the MBeanServer is particularly useful when
* <a href="MBeanServer.html#security">MBean permissions</a> are checked:
* it makes it
* possible to distinguish between an MBean named "X" in MBeanServer named
* "M1", and another MBean of the same name "X" in another MBeanServer named
* "M2".</p>
* <p>When naming MBean servers it is recommended to use a name that starts
* with a Java package name. It is also recommended that the default domain and
* the MBeanServer name be the same.</p>
*
* @since 1.5
*/
public class MBeanServerFactory {
/**
* The <a href="#MBeanServerName">MBean Server name</a> that will be
* checked by a <a href="MBeanServer.html#security">permission you need</a>
* when checking access to an MBean registered in an MBeanServer for
* which no MBeanServer name was specified.
*
* @since 1.7
*/
public final static String DEFAULT_MBEANSERVER_NAME = "default";
/*
* There are no instances of this class so don't generate the
* default public constructor.
@ -222,13 +268,78 @@ public class MBeanServerFactory {
* <code>javax.management.builder.initial</code> exists and can be
* instantiated but is not assignment compatible with {@link
* MBeanServerBuilder}.
*
* @see #createNamedMBeanServer
*/
public static MBeanServer createMBeanServer(String domain) {
checkPermission("createMBeanServer");
return createMBeanServer(null,domain);
}
final MBeanServer mBeanServer = newMBeanServer(domain);
addMBeanServer(mBeanServer);
return mBeanServer;
/**
* <p>Return a new object implementing the {@link MBeanServer}
* interface with the specified
* <a href="#MBeanServerName">MBean Server name</a>
* and default domain name. The given MBean server name
* is used in <a href="MBeanServer.html#security">security checks</a>, and
* can also be used to {@linkplain #findMBeanServerByName(java.lang.String)
* find an MBeanServer by name}. The given
* domain name is used as the domain part in the ObjectName of
* MBeans when the domain is specified by the user is null.</p>
*
* <p>The MBeanServer reference is internally kept. This will
* allow <CODE>findMBeanServer</CODE> to return a reference to
* this MBeanServer object.</p>
*
* @param mbeanServerName the name for the created
* MBeanServer. This is the name that will be included in the
* {@linkplain MBeanPermission permission you need} when checking
* <a href="MBeanServer.html#security">MBean Permissions</a> for accessing
* an MBean registered in the returned MBeanServer. The characters
* {@code ':'} (colon), {@code ';'} (semicolon), {@code '*'} (star)
* and {@code '?'} are not legal.
* It is recommended that the {@code mbeanServerName}
* be unique in the context of a JVM, and in the form of a java package
* identifier. If {@code mbeanServerName} is {@code null} then the created
* MBean Server has no name - and {@value #DEFAULT_MBEANSERVER_NAME} is used.
* Calling {@code createNamedMBeanServer(null,domain)} is equivalent
* to calling {@link #createMBeanServer(String) createMBeanServer(domain)}.
*
* @param domain the default domain name for the created
* MBeanServer. This is the value that will be returned by {@link
* MBeanServer#getDefaultDomain}. If a non null mbeanServerName is given,
* it is recommended to pass the same value as default domain.
*
* @return the newly created MBeanServer.
*
* @exception SecurityException if there is a SecurityManager and
* the caller's permissions do not include or imply <code>{@link
* MBeanServerPermission}("createMBeanServer")</code>.
*
* @exception JMRuntimeException if the property
* <code>javax.management.builder.initial</code> exists but the
* class it names cannot be instantiated through a public
* no-argument constructor; or if the instantiated builder returns
* null from its {@link MBeanServerBuilder#newMBeanServerDelegate
* newMBeanServerDelegate} or {@link
* MBeanServerBuilder#newMBeanServer newMBeanServer} methods.
*
* @exception ClassCastException if the property
* <code>javax.management.builder.initial</code> exists and can be
* instantiated but is not assignment compatible with {@link
* MBeanServerBuilder}.
*
* @exception IllegalArgumentException if the specified
* {@code mbeanServerName} is empty, or is {@code "-"}, or contains a
* character which is not legal.
*
* @exception UnsupportedOperationException if the specified
* {@code mbeanServerName} cannot be set.
*
* @since 1.7
*/
public static MBeanServer createNamedMBeanServer(String mbeanServerName,
String domain) {
return createMBeanServer(mbeanServerName, domain);
}
/**
@ -307,6 +418,88 @@ public class MBeanServerFactory {
* MBeanServerBuilder}.
*/
public static MBeanServer newMBeanServer(String domain) {
return newMBeanServer(null,domain);
}
/**
* <p>Return a new object implementing the MBeanServer interface
* with the specified <a href="#MBeanServerName">MBean server name</a>
* and default domain name, without keeping an
* internal reference to this new object. The given MBean server name
* is used in <a href="MBeanServer.html#security">security checks</a>.
* The given domain name
* is used as the domain part in the ObjectName of MBeans when the
* domain is specified by the user is null.</p>
*
* <p>No reference is kept. <CODE>findMBeanServer</CODE> and
* <CODE>findMBeanServerByName</CODE> will not
* be able to return a reference to this MBeanServer object, but
* the garbage collector will be able to remove the MBeanServer
* object when it is no longer referenced.</p>
*
* @param mbeanServerName the name for the created
* MBeanServer. This is the name that will be included in the
* {@linkplain MBeanPermission permission you need} when checking
* <a href="MBeanServer.html#security">MBean Permissions</a> for accessing
* an MBean registered in the returned MBeanServer. The characters
* {@code ':'} (colon), {@code ';'} (semicolon), {@code '*'} (star)
* and {@code '?'} are not legal.
* It is recommended that the mbeanServerName
* be unique in the context of a JVM, and in the form of a java package
* identifier. If {@code mbeanServerName} is {@code null} then the created
* MBean Server has no name - and {@value #DEFAULT_MBEANSERVER_NAME} is used.
* Calling {@code newNamedMBeanServer(null,domain)} is equivalent
* to calling {@link #newMBeanServer(String) newMBeanServer(domain)}.
*
* @param domain the default domain name for the created
* MBeanServer. This is the value that will be returned by {@link
* MBeanServer#getDefaultDomain}.
*
* @return the newly created MBeanServer.
*
* @exception SecurityException if there is a SecurityManager and the
* caller's permissions do not include or imply <code>{@link
* MBeanServerPermission}("newMBeanServer")</code>.
*
* @exception JMRuntimeException if the property
* <code>javax.management.builder.initial</code> exists but the
* class it names cannot be instantiated through a public
* no-argument constructor; or if the instantiated builder returns
* null from its {@link MBeanServerBuilder#newMBeanServerDelegate
* newMBeanServerDelegate} or {@link
* MBeanServerBuilder#newMBeanServer newMBeanServer} methods.
*
* @exception ClassCastException if the property
* <code>javax.management.builder.initial</code> exists and can be
* instantiated but is not assignment compatible with {@link
* MBeanServerBuilder}.
*
* @exception IllegalArgumentException if the specified
* {@code mbeanServerName} is empty, or is {@code "-"},
* or contains a character which is not legal.
*
* @exception UnsupportedOperationException if the specified
* {@code mbeanServerName} cannot be set.
*
* @since 1.7
*/
public static MBeanServer newNamedMBeanServer(String mbeanServerName,
String domain) {
return newMBeanServer(mbeanServerName, domain);
}
private static MBeanServer createMBeanServer(String mbeanServerName,
String domain) {
checkPermission("createMBeanServer");
final MBeanServer mBeanServer =
newMBeanServer(mbeanServerName,domain);
addMBeanServer(mBeanServer);
return mBeanServer;
}
private static MBeanServer newMBeanServer(String mbeanServerName,
String domain) {
checkPermission("newMBeanServer");
// Get the builder. Creates a new one if necessary.
@ -323,6 +516,22 @@ public class MBeanServerFactory {
"returned null";
throw new JMRuntimeException(msg);
}
// Sets the name on the delegate. For complex backward
// compatibility reasons it is not possible to give the
// name to the MBeanServerDelegate constructor.
//
// The method setMBeanServerName() will call getMBeanServerId()
// to check that the name is accurately set in the MBeanServerId.
// If not (which could happen if a custom MBeanServerDelegate
// implementation overrides getMBeanServerId() and was not updated
// with respect to JMX 2.0 spec, this method will throw an
// IllegalStateException...
//
if (!Util.isMBeanServerNameUndefined(mbeanServerName)) {
delegate.setMBeanServerName(mbeanServerName);
}
final MBeanServer mbeanServer =
mbsBuilder.newMBeanServer(domain,null,delegate);
if (mbeanServer == null) {
@ -330,6 +539,20 @@ public class MBeanServerFactory {
"MBeanServerBuilder.newMBeanServer() returned null";
throw new JMRuntimeException(msg);
}
// double check that the MBeanServer name is correctly set.
// "*" might mean that the caller doesn't have the permission
// to see the MBeanServer name.
//
final String mbsName = Util.getMBeanServerSecurityName(mbeanServer);
if (!mbsName.equals(Util.checkServerName(mbeanServerName))
&& !mbsName.equals("*")) {
throw new UnsupportedOperationException(
"can't create MBeanServer with name \""+
mbeanServerName+"\" using "+
builder.getClass().getName());
}
return mbeanServer;
}
}
@ -363,16 +586,107 @@ public class MBeanServerFactory {
ArrayList<MBeanServer> result = new ArrayList<MBeanServer>();
for (MBeanServer mbs : mBeanServerList) {
String name = mBeanServerName(mbs);
String name = mBeanServerId(mbs);
if (agentId.equals(name))
result.add(mbs);
}
return result;
}
/**
* <p>Returns a list of registered MBeanServer objects with the given name. A
* registered MBeanServer object is one that was created by one of
* the <code>createMBeanServer</code> or <code>createNamedMBeanServer</code>
* methods and not subsequently released with <code>releaseMBeanServer</code>.</p>
* <p>See the section about <a href="#MBeanServerName">MBean Server names</a>
* above.</p>
*
* @param mbeanServerName The name of the MBeanServer to
* retrieve. If this parameter is null, all registered MBeanServers
* in this JVM are returned.
* Otherwise, only those MBeanServers that have a name
* matching <code>mbeanServerName</code> are returned: this
* parameter can be a pattern, where {@code '*'} matches any
* sequence of characters and {@code '?'} matches any character.<br>
* The name of an MBeanServer, if specified, is embedded in the
* <code>MBeanServerId</code> attribute of its delegate MBean:
* this method will parse the <code>MBeanServerId</code> to get the
* MBeanServer name. If this parameter is equal to {@code "*"} then
* all registered MBeanServers in this JVM are returned, whether they have
* a name or not: {@code findMBeanServerByName(null)},
* {@code findMBeanServerByName("*")} and {@code findMBeanServer(null)},
* are equivalent. It is also possible to get all MBeanServers for which
* no name was specified by calling <code>findMBeanServerByName({@value
* #DEFAULT_MBEANSERVER_NAME})</code>.
*
* @return A list of MBeanServer objects.
*
* @exception SecurityException if there is a SecurityManager and the
* caller's permissions do not include or imply <code>{@link
* MBeanServerPermission}("findMBeanServer")</code>.
*
* @see #getMBeanServerName(MBeanServer)
* @since 1.7
*/
public synchronized static
List<MBeanServer> findMBeanServerByName(String mbeanServerName) {
checkPermission("findMBeanServer");
if (mbeanServerName==null || "*".equals(mbeanServerName))
return new ArrayList<MBeanServer>(mBeanServerList);
// noname=true iff we are looking for MBeanServers for which no name
// were specified.
ArrayList<MBeanServer> result = new ArrayList<MBeanServer>();
for (MBeanServer mbs : mBeanServerList) {
final String name = Util.getMBeanServerSecurityName(mbs);
if (Util.wildmatch(name, mbeanServerName)) result.add(mbs);
}
return result;
}
/**
* Returns the name of the MBeanServer embedded in the MBeanServerId of
* the given {@code server}. If the given MBeanServerId doesn't contain
* any name, {@value #DEFAULT_MBEANSERVER_NAME} is returned.
* The MBeanServerId is expected to be of the form:
* {@code *[;mbeanServerName=<mbeanServerName>[;*]]}
* <br>where {@code *} denotes any sequence of characters, and {@code [ ]}
* indicate optional parts.
* </p>
* <p>For instance, if an MBeanServer is created using {@link
* #createNamedMBeanServer(java.lang.String, java.lang.String)
* server =
* MBeanServerFactory.createNamedMBeanServer("com.mycompany.myapp.server1",
* null)} then {@code MBeanServerFactory.getMBeanServerName(server)}
* will return {@code "com.mycompany.myapp.server1"} and
* <code>server.getAttribute({@link
* javax.management.MBeanServerDelegate#DELEGATE_NAME
* MBeanServerDelegate.DELEGATE_NAME}, "MBeanServerId")</code> will return
* something like
* {@code "myhost_1213353064145;mbeanServerName=com.mycompany.myapp.server1"}.
* </p>
* <p>See the section about <a href="#MBeanServerName">MBean Server names</a>
* above.</p>
* @param server A named (or unnamed) MBeanServer.
* @return the name of the MBeanServer if found, or
* {@value #DEFAULT_MBEANSERVER_NAME} if no name is
* present in its MBeanServerId, or "*" if its
* MBeanServerId couldn't be obtained. Returning "*" means that
* only {@link MBeanPermission}s that allow all MBean Server names
* will apply to this MBean Server.
* @see MBeanServerDelegate
* @since 1.7
*/
public static String getMBeanServerName(MBeanServer server) {
return Util.getMBeanServerSecurityName(server);
}
/**
* Return the ClassLoaderRepository used by the given MBeanServer.
* This method is equivalent to {@link MBeanServer#getClassLoaderRepository() server.getClassLoaderRepository()}.
* This method is equivalent to {@link
* MBeanServer#getClassLoaderRepository() server.getClassLoaderRepository()}.
* @param server The MBeanServer under examination. Since JMX 1.2,
* if <code>server</code> is <code>null</code>, the result is a
* {@link NullPointerException}. This behavior differs from what
@ -391,11 +705,13 @@ public class MBeanServerFactory {
return server.getClassLoaderRepository();
}
private static String mBeanServerName(MBeanServer mbs) {
private static String mBeanServerId(MBeanServer mbs) {
try {
return (String) mbs.getAttribute(MBeanServerDelegate.DELEGATE_NAME,
"MBeanServerId");
} catch (JMException e) {
JmxProperties.MISC_LOGGER.finest(
"Ignoring exception while getting MBeanServerId: "+e);
return null;
}
}
@ -453,8 +769,8 @@ public class MBeanServerFactory {
**/
private static MBeanServerBuilder newBuilder(Class builderClass) {
try {
final Object builder = builderClass.newInstance();
return (MBeanServerBuilder)builder;
final Object abuilder = builderClass.newInstance();
return (MBeanServerBuilder)abuilder;
} catch (RuntimeException x) {
throw x;
} catch (Exception x) {

View file

@ -469,8 +469,8 @@ public class MemoryPool
<em>J</em>, then <em>J</em> cannot be the type of a method
parameter or return value in an MXBean interface.</p>
<p>If there is a way to convert <em>opendata(J)</em> back to
<em>J</em> then we say that <em>J</em> is
<p id="reconstructible-def">If there is a way to convert
<em>opendata(J)</em> back to <em>J</em> then we say that <em>J</em> is
<em>reconstructible</em>. All method parameters in an MXBean
interface must be reconstructible, because when the MXBean
framework is invoking a method it will need to convert those

View file

@ -27,6 +27,8 @@ package javax.management;
import com.sun.jmx.mbeanserver.GetPropertyAction;
import com.sun.jmx.mbeanserver.Util;
import com.sun.jmx.namespace.serial.JMXNamespaceContext;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
@ -222,6 +224,17 @@ import java.util.Map;
@SuppressWarnings("serial") // don't complain serialVersionUID not constant
public class ObjectName implements Comparable<ObjectName>, QueryExp {
/**
* The sequence of characters used to separate name spaces in a name space
* path.
*
* @see javax.management.namespace
* @since 1.7
**/
public static final String NAMESPACE_SEPARATOR = "//";
private static final int NAMESPACE_SEPARATOR_LENGTH =
NAMESPACE_SEPARATOR.length();
/**
* A structure recording property structure and
* proposing minimal services
@ -251,16 +264,17 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
/**
* Returns a key string for receiver key
*/
String getKeyString(String name) {
return name.substring(_key_index, _key_index + _key_length);
String getKeyString(String name, int offset) {
final int start = _key_index+offset;
return name.substring(start, start + _key_length);
}
/**
* Returns a value string for receiver key
*/
String getValueString(String name) {
int in_begin = _key_index + _key_length + 1;
int out_end = in_begin + _value_length;
String getValueString(String name, int offset) {
final int in_begin = _key_index + offset + _key_length + 1;
final int out_end = in_begin + _value_length;
return name.substring(in_begin, out_end);
}
}
@ -393,6 +407,45 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
*/
private transient boolean _property_value_pattern = false;
private ObjectName(String newDomain, ObjectName aname)
throws MalformedObjectNameException{
copyToOtherDomain(newDomain,aname);
}
private void copyToOtherDomain(String domain, ObjectName aname)
throws MalformedObjectNameException, NullPointerException {
// The domain cannot be null
if (domain == null)
throw new NullPointerException("domain cannot be null");
// The key property list cannot be null
if (aname == null)
throw new MalformedObjectNameException(
"key property list cannot be empty");
// checks domain validity. A side effect of this method is also to
// set the _domain_pattern flag.
if (!isDomain(domain))
throw new MalformedObjectNameException("Invalid domain: " + domain);
// init canonicalname
_domain_length = domain.length();
_canonicalName = (domain +
aname._canonicalName.substring(aname._domain_length)).intern();
_kp_array = aname._kp_array;
_ca_array = aname._ca_array;
_propertyList = aname._propertyList;
_property_list_pattern = aname._property_list_pattern;
_property_value_pattern = aname._property_value_pattern;
// TODO remove this hack
// if (toString().endsWith("//javax.management.service:type1=event_client_delegeate_mbean,type2=default")) {
// Thread.currentThread().dumpStack();
// throw new Error("************************ Gotcha!");
//}
}
// Instance private fields <=======================================
// Private fields <========================================
@ -435,10 +488,10 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
}
// initialize parsing of the string
char[] name_chars = name.toCharArray();
int len = name_chars.length;
char[] canonical_chars = new char[len]; // canonical form will be same
// length at most
final char[] name_chars = name.toCharArray();
final int len = name_chars.length;
final char[] canonical_chars = new char[len]; // canonical form will
// be same length at most
int cname_index = 0;
int index = 0;
char c, c1;
@ -637,10 +690,12 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
// we got the key and value part, prepare a property for this
if (!value_pattern) {
prop = new Property(key_index, key_length, value_length);
prop = new Property(key_index-_domain_length,
key_length, value_length);
} else {
_property_value_pattern = true;
prop = new PatternProperty(key_index, key_length, value_length);
prop = new PatternProperty(key_index-_domain_length,
key_length, value_length);
}
key_name = name.substring(key_index, key_index + key_length);
@ -725,12 +780,12 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
boolean value_pattern = checkValue(value);
sb.append(value);
if (!value_pattern) {
prop = new Property(key_index,
prop = new Property(key_index-_domain_length,
key.length(),
value.length());
} else {
_property_value_pattern = true;
prop = new PatternProperty(key_index,
prop = new PatternProperty(key_index-_domain_length,
key.length(),
value.length());
}
@ -810,9 +865,9 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
prop = _ca_array[i];
// length of prop including '=' char
prop_len = prop._key_length + prop._value_length + 1;
System.arraycopy(specified_chars, prop._key_index,
System.arraycopy(specified_chars, prop._key_index+_domain_length,
canonical_chars, prop_index, prop_len);
prop.setKeyIndex(prop_index);
prop.setKeyIndex(prop_index-_domain_length);
prop_index += prop_len;
if (i != last_index) {
canonical_chars[prop_index] = ',';
@ -1031,33 +1086,6 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
k[endKey] + "'");
}
/*
* Tests whether string s is matched by pattern p.
* Supports "?", "*" each of which may be escaped with "\";
* Not yet supported: internationalization; "\" inside brackets.<P>
* Wildcard matching routine by Karl Heuer. Public Domain.<P>
*/
private static boolean wildmatch(char[] s, char[] p, int si, int pi) {
char c;
final int slen = s.length;
final int plen = p.length;
while (pi < plen) { // While still string
c = p[pi++];
if (c == '?') {
if (++si > slen) return false;
} else if (c == '*') { // Wildcard
if (pi >= plen) return true;
do {
if (wildmatch(s,p,si,pi)) return true;
} while (++si < slen);
return false;
} else {
if (si >= slen || c != s[si++]) return false;
}
}
return (si == slen);
}
// Category : Internal utilities <==============================
@ -1177,15 +1205,43 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
cn = (String)in.readObject();
}
final JMXNamespaceContext ctxt =
JMXNamespaceContext.getDeserializationContext();
try {
construct(cn);
construct(changeContext(ctxt,cn));
} catch (NullPointerException e) {
throw new InvalidObjectException(e.toString());
} catch (IllegalArgumentException e) {
throw new InvalidObjectException(e.toString());
} catch (MalformedObjectNameException e) {
throw new InvalidObjectException(e.toString());
}
}
private String changeContext(JMXNamespaceContext context, String nameString) {
final String old = context.prefixToRemove;
final String nw = context.prefixToAdd;
final int ol = old.length();
if (nameString.startsWith(NAMESPACE_SEPARATOR)) return nameString;
if (ol>0) {
if (!nameString.startsWith(old) ||
!nameString.startsWith(NAMESPACE_SEPARATOR,ol))
throw new IllegalArgumentException(
"Serialized ObjectName does not start with " + old +
": " + nameString);
nameString = nameString.substring(ol+NAMESPACE_SEPARATOR_LENGTH);
}
if (!nw.equals("")) {
nameString = nw + NAMESPACE_SEPARATOR + nameString;
}
// TODO remove this hack
// if (nameString.endsWith("//javax.management.service:type1=event_client_delegeate_mbean,type2=default")) {
// System.err.println("old="+old+", nw="+nw);
// Thread.currentThread().dumpStack();
// throw new Error("************************ Gotcha!");
// }
return nameString;
}
/**
* Serializes an {@link ObjectName} to an {@link ObjectOutputStream}.
@ -1248,15 +1304,22 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
private void writeObject(ObjectOutputStream out)
throws IOException {
final JMXNamespaceContext ctxt =
JMXNamespaceContext.getSerializationContext();
if (compat)
{
// Serializes this instance in the old serial form
// Read CR 6441274 before making any changes to this code
ObjectOutputStream.PutField fields = out.putFields();
fields.put("domain", _canonicalName.substring(0, _domain_length));
final String domain =
changeContext(ctxt,_canonicalName.substring(0, _domain_length));
final String cn =
changeContext(ctxt,_canonicalName);
fields.put("domain", domain);
fields.put("propertyList", getKeyPropertyList());
fields.put("propertyListString", getKeyPropertyListString());
fields.put("canonicalName", _canonicalName);
fields.put("canonicalName", cn);
fields.put("pattern", (_domain_pattern || _property_list_pattern));
fields.put("propertyPattern", _property_list_pattern);
out.writeFields();
@ -1266,7 +1329,8 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
// Serializes this instance in the new serial form
//
out.defaultWriteObject();
out.writeObject(getSerializedNameString());
out.writeObject(changeContext(ctxt,getSerializedNameString()));
}
}
@ -1396,6 +1460,27 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
return Util.newObjectName(name.getSerializedNameString());
}
/**
* Returns an {@code ObjectName} that is the same as this one but
* with the specified domain.
* This method preserves the original key order in the new instance.
* If the provided name has a key property pattern, it will also be
* preserved in the returned instance.
*
* @param newDomain The new domain for the returned instance;
* must not be null.
* @return A new {@code ObjectName} that is the same as {@code this}
* except the domain is {@code newDomain}.
* @throws NullPointerException if {@code newDomain} is null.
* @throws MalformedObjectNameException if the new domain is syntactically
* illegal.
* @since 1.7
**/
public final ObjectName withDomain(String newDomain)
throws NullPointerException, MalformedObjectNameException {
return new ObjectName(newDomain, this);
}
/**
* Construct an object name from the given string.
*
@ -1550,7 +1635,7 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
throw new NullPointerException("key property can't be null");
for (int i = 0; i < _ca_array.length; i++) {
Property prop = _ca_array[i];
String key = prop.getKeyString(_canonicalName);
String key = prop.getKeyString(_canonicalName,_domain_length);
if (key.equals(property))
return (prop instanceof PatternProperty);
}
@ -1630,8 +1715,10 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
Property prop;
for (int i = len - 1; i >= 0; i--) {
prop = _ca_array[i];
_propertyList.put(prop.getKeyString(_canonicalName),
prop.getValueString(_canonicalName));
_propertyList.put(prop.getKeyString(_canonicalName,
_domain_length),
prop.getValueString(_canonicalName,
_domain_length));
}
}
}
@ -1716,7 +1803,8 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
}
}
return new String(dest_chars);
final String name = new String(dest_chars);
return name;
}
/**
@ -1734,7 +1822,7 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
if (_kp_array.length == 0) return offset;
final char[] dest_chars = data;
final char[] value = _canonicalName.toCharArray();
final char[] value = canonicalChars;
int index = offset;
final int len = _kp_array.length;
@ -1742,7 +1830,7 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
for (int i = 0; i < len; i++) {
final Property prop = _kp_array[i];
final int prop_len = prop._key_length + prop._value_length + 1;
System.arraycopy(value, prop._key_index, dest_chars, index,
System.arraycopy(value, prop._key_index+_domain_length, dest_chars, index,
prop_len);
index += prop_len;
if (i < last ) dest_chars[index++] = ',';
@ -1816,7 +1904,7 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
// (because usage of intern())
ObjectName on = (ObjectName) object;
String on_string = on._canonicalName;
if (_canonicalName == on_string) return true;
if (_canonicalName == on_string) return true; // ES: OK
// Because we are sharing canonical form between object names,
// we have finished the comparison at this stage ==> unequal
@ -1997,9 +2085,9 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
private final boolean matchDomains(ObjectName name) {
if (_domain_pattern) {
// wildmatch domains
final char[] dom_pattern = getDomain().toCharArray();
final char[] dom_string = name.getDomain().toCharArray();
return wildmatch(dom_string,dom_pattern,0,0);
// This ObjectName is the pattern
// The other ObjectName is the string.
return Util.wildpathmatch(name.getDomain(),getDomain());
}
return getDomain().equals(name.getDomain());
}
@ -2025,7 +2113,7 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
// index in receiver
//
final Property p = props[i];
final String k = p.getKeyString(cn);
final String k = p.getKeyString(cn,_domain_length);
final String v = nameProps.get(k);
// Did we find a value for this key ?
//
@ -2034,15 +2122,13 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
//
if (_property_value_pattern && (p instanceof PatternProperty)) {
// wildmatch key property values
final char[] val_pattern =
p.getValueString(cn).toCharArray();
final char[] val_string = v.toCharArray();
if (wildmatch(val_string,val_pattern,0,0))
// p is the property pattern, v is the string
if (Util.wildmatch(v,p.getValueString(cn,_domain_length)))
continue;
else
return false;
}
if (v.equals(p.getValueString(cn))) continue;
if (v.equals(p.getValueString(cn,_domain_length))) continue;
return false;
}
return true;
@ -2109,6 +2195,10 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
* @since 1.6
*/
public int compareTo(ObjectName name) {
// Quick optimization:
//
if (name == this) return 0;
// (1) Compare domains
//
int domainValue = this.getDomain().compareTo(name.getDomain());

View file

@ -29,6 +29,7 @@ import com.sun.jmx.event.DaemonThreadFactory;
import com.sun.jmx.event.LeaseRenewer;
import com.sun.jmx.event.ReceiverBuffer;
import com.sun.jmx.event.RepeatedSingletonJob;
import com.sun.jmx.namespace.JMXNamespaceUtils;
import com.sun.jmx.mbeanserver.PerThreadGroupPool;
import com.sun.jmx.remote.util.ClassLogger;
@ -1063,6 +1064,24 @@ public class EventClient implements EventConsumer, NotificationManager {
return clientId;
}
/**
* Returns a JMX Connector that will use an {@link EventClient}
* to subscribe for notifications. If the server doesn't have
* an {@link EventClientDelegateMBean}, then the connector will
* use the legacy notification mechanism instead.
*
* @param wrapped The underlying JMX Connector wrapped by the returned
* connector.
*
* @return A JMX Connector that will uses an {@link EventClient}, if
* available.
*
* @see EventClient#getEventClientConnection(MBeanServerConnection)
*/
public static JMXConnector withEventClient(final JMXConnector wrapped) {
return JMXNamespaceUtils.withEventClient(wrapped);
}
private static final PerThreadGroupPool<ScheduledThreadPoolExecutor>
leaseRenewerThreadPool = PerThreadGroupPool.make();
}

View file

@ -721,7 +721,10 @@ public class EventClientDelegate implements EventClientDelegateMBean {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
ObjectInstance oi = (ObjectInstance) AccessController.doPrivileged(
final String serverName = getMBeanServerName();
ObjectInstance oi = (ObjectInstance)
AccessController.doPrivileged(
new PrivilegedExceptionAction<Object>() {
public Object run()
throws InstanceNotFoundException {
@ -731,6 +734,7 @@ public class EventClientDelegate implements EventClientDelegateMBean {
String classname = oi.getClassName();
MBeanPermission perm = new MBeanPermission(
serverName,
classname,
null,
name,
@ -746,6 +750,20 @@ public class EventClientDelegate implements EventClientDelegateMBean {
return true;
}
private String getMBeanServerName() {
if (mbeanServerName != null) return mbeanServerName;
else return (mbeanServerName = getMBeanServerName(mbeanServer));
}
private static String getMBeanServerName(final MBeanServer server) {
final PrivilegedAction<String> action = new PrivilegedAction<String>() {
public String run() {
return Util.getMBeanServerSecurityName(server);
}
};
return AccessController.doPrivileged(action);
}
// ------------------------------------
// private variables
// ------------------------------------

View file

@ -0,0 +1,382 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package javax.management.namespace;
import java.io.IOException;
import javax.management.ListenerNotFoundException;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import static javax.management.namespace.JMXNamespaces.NAMESPACE_SEPARATOR;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServer;
import javax.management.MBeanServerDelegate;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
/**
* A special {@link JMXNamespace} that can handle part of
* the MBeanServer local name space.
* <p>
* A {@code JMXDomain} makes a domain <i>X</i> of a
* {@linkplain #getSourceServer() source MBean server} appear in the same domain
* <i>X</i> of a containing {@code MBeanServer} in which the
* {@code JMXDomain} MBean {@linkplain #getMBeanServer() is registered}.
* </p>
* <p>
* The JMX infrastructure of the containing {@code MBeanServer} takes care of
* routing all calls to MBeans whose names have domain <i>X</i> to the
* {@linkplain #getSourceServer() source MBean server} exported by the
* {@code JMXDomain} MBean in charge of domain <i>X</i>.
* </p>
* <p>
* The {@linkplain #getSourceServer() source MBean server} of a {@code JMXDomain}
* can, but need not be a regular {@code MBeanServer} created through
* the {@link javax.management.MBeanServerFactory}. It could also be,
* for instance, an instance of a subclass of {@link MBeanServerSupport},
* or a custom object implementing the {@link MBeanServer} interface.
* </p>
*
* <h4>Differences between {@code JMXNamespace} and {@code JMXDomain}</h4>
*
* <p>
* A {@code JMXDomain} is a special kind of {@code JMXNamespace}.
* A {@code JMXNamespace} such as {@code foo//} is triggered by an
* {@code ObjectName} that begins with the string {@code foo//}, for example
* {@code foo//bar:type=Baz}. A {@code JMXDomain} such as {@code foo} is
* triggered by an {@code ObjectName} with that exact domain, for example
* {@code foo:type=Baz}. A client can immediately see that an MBean is
* handled by a {@code JMXNamespace} because of the {@code //} in the name.
* A client cannot see whether a name such as {@code foo:type=Baz} is an
* ordinary MBean or is handled by a {@code JMXDomain}.
* </p>
*
* <p>
* A {@linkplain MBeanServer#queryNames query} on the containing {@code
* MBeanserver} will return all MBeans from the {@code JMXDomain} that match
* the query. In particular, {@code queryNames(null, null)} will return all
* MBeans including those from {@code JMXDomain} domains. On the other hand,
* a query will not include MBeans from a {@code JMXNamespace} unless the
* {@code ObjectName} pattern in the query starts with the name of that
* namespace.
* </p>
*
* <h4 id="security">Permission checks</h4>
*
* <p>
* When a JMXDomain MBean is registered in a containing
* MBean server created through the default {@link
* javax.management.MBeanServerBuilder}, and if a {@link
* SecurityManager SecurityManager} is
* {@linkplain System#getSecurityManager() present}, the containing MBeanServer will
* check an {@link javax.management.MBeanPermission} before invoking
* any method on the {@linkplain #getSourceServer() source MBeanServer} of the
* JMXDomain.
* </p>
*
* <p>First, if there is no security manager ({@link
* System#getSecurityManager()} is null), that containing
* {@code MBeanServer} is free not to make any checks.
* </p>
*
* <p>
* Assuming that there is a security manager, or that the
* implementation chooses to make checks anyway, the containing
* {@code MBeanServer} will perform
* {@link javax.management.MBeanPermission MBeanPermission} checks
* for access to the MBeans in domain <i>X</i> handled by a {@code JMXDomain}
* in the same way that it would do for MBeans registered in its own local
* repository, and as <a href="../MBeanServer.html#security">described in
* the MBeanServer interface</a>, with the following exceptions:
* </p>
*
* <p>
* For those permissions that require a {@code className}, the
* <code>className</code> is the
* string returned by {@link #getSourceServer() getSourceServer()}.
* {@link MBeanServer#getObjectInstance(ObjectName)
* getObjectInstance(mbeanName)}.
* {@link javax.management.ObjectInstance#getClassName() getClassName()},
* except for {@code createMBean} and {@code registerMBean} operations,
* for which the permission checks are performed as follows:
* </p>
* <ul>
* <li><p>For {@code createMBean} operations, the {@code className} of the
* permission you need is the {@code className} passed as first parameter
* to {@code createMBean}.</p>
*
* <li><p>For {@code registerMBean} operations, the {@code className} of the
* permission you need is the name of the class of the mbean object, as
* returned by {@code mbean.getClass().getClassName()}, where
* {@code mbean} is the mbean reference passed as first parameter
* to {@code registerMBean}.</p>
*
* <li><p>In addition, for {@code createMBean} and {@code registerMBean}, the
* permission you need is checked with the {@linkplain ObjectName object name} of
* the mbean that is passed as second parameter to the {@code createMBean} or
* {@code registerMBean} operation.
* </p>
*
* <li><p>Contrarily to what is done for regular MBeans registered in the
* MBeanServer local repository, the containing MBeanServer will not
* check the {@link javax.management.MBeanTrustPermission#MBeanTrustPermission(String)
* MBeanTrustPermission("register")} against the protection domain
* of the MBean's class. This check can be performed by the
* {@linkplain #getSourceServer source MBean server} implementation,
* if necessary.
* </p>
* </ul>
*
* <p>If a security check fails, the method throws {@link
* SecurityException}.</p>
*
* <p>For methods that can throw {@link InstanceNotFoundException},
* this exception is thrown for a non-existent MBean, regardless of
* permissions. This is because a non-existent MBean has no
* <code>className</code>.</p>
*
* All these checks are performed by the containing {@code MBeanServer},
* before accessing the JMXDomain {@linkplain #getSourceServer source MBean server}.
* The implementation of the JMXDomain {@linkplain #getSourceServer source MBean
* server} is free to make any additional checks. In fact, if the JMXDomain
* {@linkplain #getSourceServer source MBean server} is an {@code MBeanServer}
* obtained through the {@link javax.management.MBeanServerFactory}, it will
* again make permission checks as described in the
* <a href="../MBeanServer.html#security">MBeanServer</a> interface.
*
* <p>See the <a href="../MBeanServer.html#security">MBeanServer</a> interface
* for more details on permission checks.</p>
*
* @since 1.7
*/
public class JMXDomain extends JMXNamespace {
/**
* This constant contains the value of the {@code type}
* key used in defining a standard JMXDomain MBean object name.
* By definition, a standard JMXDomain MBean object name must be of
* the form:
* <pre>
* {@code "<domain>:"}+{@value javax.management.namespace.JMXDomain#TYPE_ASSIGNMENT}
* </pre>
*/
public static final String TYPE = "JMXDomain";
/**
* This constant contains the value of the standard
* {@linkplain javax.management.ObjectName#getKeyPropertyListString() key
* property list string} for JMXDomain MBean object names.
* By definition, a standard JMXDomain MBean object name must be of
* the form:
* <pre>
* {@code <domain>}+":"+{@value javax.management.namespace.JMXDomain#TYPE_ASSIGNMENT}
* </pre>
*/
public static final String TYPE_ASSIGNMENT = "type="+TYPE;
/**
* Creates a new instance of JMXDomain. The MBeans contained in this
* domain are handled by the {@code virtualServer} object given to
* this constructor. Frequently, this will be an instance of
* {@link MBeanServerSupport}.
* @param virtualServer The virtual server that acts as a container for
* the MBeans handled by this JMXDomain object. Frequently, this will
* be an instance of {@link MBeanServerSupport}
* @see JMXNamespace#JMXNamespace(MBeanServer)
*/
public JMXDomain(MBeanServer virtualServer) {
super(virtualServer);
}
/**
* Return the name of domain handled by this JMXDomain.
* @return the domain handled by this JMXDomain.
* @throws IOException - if the domain cannot be determined,
* for instance, if the MBean is not registered yet.
*/
@Override
public final String getDefaultDomain() {
final ObjectName name = getObjectName();
if (name == null)
throw new IllegalStateException("DefaultDomain is not yet known");
final String dom = name.getDomain();
return dom;
}
/**
* Returns a singleton array, containing the only domain handled by
* this JMXDomain object. This is
* {@code new String[] {getDefaultDomain()}}.
* @return the only domain handled by this JMXDomain.
* @throws IOException if the domain cannot be determined,
* for instance, if the MBean is not registered yet.
* @see #getDefaultDomain()
*/
@Override
public final String[] getDomains() {
return new String[] {getDefaultDomain()};
}
/**
* This method returns the number of MBeans in the domain handled
* by this JMXDomain object. The default implementation is:
* <pre>
* getSourceServer().queryNames(
* new ObjectName(getObjectName().getDomain()+":*"), null).size();
* </pre>
* If this JMXDomain is not yet registered, this method returns 0.
* Subclasses can override the above behavior and provide a better
* implementation.
* <p>
* The getMBeanCount() method is called when computing the number
* of MBeans in the {@linkplain #getMBeanServer() containing MBeanServer}.
* @return the number of MBeans in this domain, or 0.
*/
@Override
public Integer getMBeanCount() {
final ObjectName name = getObjectName();
if (name == null) return 0;
try {
return getSourceServer().
queryNames(ObjectName.WILDCARD.withDomain(name.getDomain()),
null).size();
} catch (RuntimeException x) {
throw x;
} catch (Exception x) {
throw new RuntimeException("Unexpected exception: "+x,x);
}
}
/**
* Return a canonical handler name for the provided local
* <var>domain</var> name, or null if the provided domain name is
* {@code null}.
* If not null, the handler name returned will be
* {@code domain+":type="+}{@link #TYPE TYPE}, for example
* {@code foo:type=JMXDomain}.
* @param domain A domain name
* @return a canonical ObjectName for a domain handler.
* @throws IllegalArgumentException if the provided
* <var>domain</var> is not valid - e.g it contains "//".
*/
public static ObjectName getDomainObjectName(String domain) {
if (domain == null) return null;
if (domain.contains(NAMESPACE_SEPARATOR))
throw new IllegalArgumentException(domain);
try {
return ObjectName.getInstance(domain, "type", TYPE);
} catch (MalformedObjectNameException x) {
throw new IllegalArgumentException(domain,x);
}
}
/**
* Validate the ObjectName supplied to preRegister.
* This method is introduced to allow standard subclasses to use
* an alternate naming scheme. For instance - if we want to
* reuse JMXNamespace in order to implement sessions...
* It is however only available for subclasses in this package.
**/
@Override
ObjectName validateHandlerName(ObjectName supliedName) {
if (supliedName == null)
throw new IllegalArgumentException("Must supply a valid name");
final String dirName = JMXNamespaces.
normalizeNamespaceName(supliedName.getDomain());
final ObjectName handlerName = getDomainObjectName(dirName);
if (!supliedName.equals(handlerName))
throw new IllegalArgumentException("invalid name space name: "+
supliedName);
return supliedName;
}
/**
* This method is called by the JMX framework to register a
* NotificationListener that will forward {@linkplain
* javax.management.MBeanServerNotification mbean server notifications}
* through the delegate of the {@linkplain #getMBeanServer()
* containing MBeanServer}.
* The default implementation of this method is to call
* <pre>
* getSourceServer().addNotificationListener(
* MBeanServerDelegate.DELEGATE_NAME, listener, filter, null);
* </pre>
* Subclasses can redefine this behavior if needed. In particular,
* subclasses can send their own instances of {@link
* javax.management.MBeanServerNotification} by calling
* {@code listener.handleNotification()}.
*
* @param listener The MBeanServerNotification listener for this domain.
* @param filter A notification filter.
*/
public void addMBeanServerNotificationListener(
NotificationListener listener, NotificationFilter filter) {
try {
getSourceServer().addNotificationListener(
MBeanServerDelegate.DELEGATE_NAME, listener, filter, null);
} catch(InstanceNotFoundException x) {
throw new UnsupportedOperationException(
"Unexpected exception: " +
"Emission of MBeanServerNotification disabled.", x);
}
}
/**
* This method is called by the JMX framework to remove the
* NotificationListener that was added with {@link
* #addMBeanServerNotificationListener addMBeanServerNotificationListener}.
* The default implementation of this method is to call
* <pre>
* getSourceServer().removeNotificationListener(
* MBeanServerDelegate.DELEGATE_NAME, listener);
* </pre>
* Subclasses can redefine this behavior if needed.
*
* @param listener The MBeanServerNotification listener for this domain.
* @throws ListenerNotFoundException if the listener is not found.
*/
public void removeMBeanServerNotificationListener(
NotificationListener listener)
throws ListenerNotFoundException {
try {
getSourceServer().removeNotificationListener(
MBeanServerDelegate.DELEGATE_NAME, listener);
} catch(InstanceNotFoundException x) {
throw new UnsupportedOperationException(
"Unexpected exception: " +
"Emission of MBeanServerNotification disabled.", x);
}
}
}

View file

@ -0,0 +1,660 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package javax.management.namespace;
import java.io.IOException;
import java.util.UUID;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.ObjectName;
/**
* MBean Servers can be federated into a single hierarchical name space:
* A JMXNamespace is an MBean that handles a sub name space in that
* hierarchical name space.
* <p>
* A name space is created simply by registering a {@code JMXNamespace}
* MBean in the MBean Server. The name of the created name space is defined
* by the {@linkplain JMXNamespaces#getNamespaceObjectName name of the JMXNamespace}
* that handles it. A name space is equivalent to
* an MBean Server within an MBean Server. When creating a {@code JMXNamespace},
* the MBean Server within is passed to the constructor.
* </p>
* <p>
* The {@code JMXNamespace} class is the base class for implementing
* all name space handlers. All name space handlers must be instances of
* {@code JMXNamespace} or a subclass of it.
* </p>
* <p>
* A concrete example of a {@code JMXNamespace} MBean subclass
* is the {@link JMXRemoteNamespace JMXRemoteNamespace} MBean which
* is able to mirror all MBeans contained in a remote MBean server known by its
* {@link javax.management.remote.JMXServiceURL}.
* </p>
* <p>
* You can create a local namespace by supplying a newly created MBean Server
* to an instance of {@code JMXNamespace}. For instance:
* <pre>
* final String namespace = "foo";
* final ObjectName namespaceName = {@link JMXNamespaces#getNamespaceObjectName
* JMXNamespaces.getNamespaceObjectName(namespace)};
* server.registerMBean(new JMXNamespace(MBeanServerFactory.newMBeanServer()),
* namespaceName);
* </pre>
* </p>
* <p>
* <u>Note:</u> A JMXNamespace MBean cannot be registered
* simultaneously in two different
* MBean servers, or indeed in the same MBean Server with two
* different names. It is however possible to give the same MBeanServer
* instance to two different JMXNamespace MBeans, and thus create a graph
* rather than a tree.
* </p>
*
* <p>To view the content of a namespace, you will usually use an
* instance of {@link JMXNamespaceView}. For instance, given the
* namespace {@code "foo"} created above, you would do:
* </p>
* <pre>
* final JMXNamespaceView view = new JMXNamespaceView(server);
* System.out.println("List of namespaces: "+Arrays.toString({@link JMXNamespaceView#list() view.list()}));
*
* final JMXNamespaceView foo = {@link JMXNamespaceView#down view.down("foo")};
* System.out.println({@link JMXNamespaceView#where() foo.where()}+" contains: " +
* {@link JMXNamespaceView#getMBeanServerConnection foo.getMBeanServerConnection()}.queryNames(null,null));
* </pre>
*
* <h2 id="PermissionChecks">JMX Namespace Permission Checks</h2>
*
* <p>A special {@link JMXNamespacePermission} is defined to check access
* to MBean within namespaces.</p>
* <p>When a JMXNamespace MBean is registered in an
* MBean server created through the default {@link
* javax.management.MBeanServerBuilder}, and if a {@link
* SecurityManager SecurityManager} is
* {@linkplain System#getSecurityManager() present}, the MBeanServer will
* check a {@link JMXNamespacePermission} before invoking
* any method on the {@linkplain #getSourceServer source MBeanServer} of the
* JMXNamespace.
* {@linkplain JMXNamespacePermission JMX Namespace Permissions} are similar to
* {@linkplain javax.management.MBeanPermission MBean Permissions}, except
* that you usually cannot specify an MBean class name. You can however
* specify object name patterns - which will allow you for example to only grant
* permissions for MBeans having a specific {@code type=<MBeanType>} key
* in their object name.
* <p>
* Another difference is that {@link JMXNamespacePermission
* JMXNamespacePermission} also specifies from which namespace and which
* MBean server the permission is granted.
* </p>
* <p>In the rest of this document, the following terms are used:</p>
* <ul>
* <li id="MBeanServerName"><p>{@code server name} is the
* <a href="../MBeanServerFactory.html#MBeanServerName">name of the
* MBeanServer</a> in which the permission is granted.
* The name of an {@code MBeanServer} can be obtained by calling {@link
* javax.management.MBeanServerFactory#getMBeanServerName
* MBeanServerFactory.getMBeanServerName(mbeanServer)}
* </p>
* <li id="NamespaceName"><p>{@code namespace} is the name of the namespace
* in the <a href="#MBeanServerName">named MBean server</a> for which the
* permission is granted. It doesn't contain any
* {@link JMXNamespaces#NAMESPACE_SEPARATOR namespace separator}.
* </p>
* <li id="MBeanName"><p>{@code mbean} is the name
* of the MBean in that {@code namespace}. This is the name of the MBean
* in the namespace's {@link JMXNamespace#getSourceServer() source mbean server}.
* It might contain no, one, or several {@link
* JMXNamespaces#NAMESPACE_SEPARATOR namespace separators}.
* </p>
* </ul>
*
* <p>For instance let's assume that some piece of code calls:</p>
* <pre>
* final MBeanServer mbeanServer = ...;
* final ObjectName name = new ObjectName("a//b//c//D:k=v");
* mbeanServer.getAttribute(name,"Foo");
* </pre>
* <p>
* Assuming that there is a security manager, or that the
* implementation chooses to make checks anyway, the checks that will
* be made in that case are:
* </p>
* <ol>
* <li id="check1">
* <code>JMXNamespacePermission(mbeanServerName, "Foo", "<b>a//</b>b//c//D:k=v",
* "getAttribute")</code>
* (where {@code mbeanServerName=MBeanServerFactory.getMBeanServerName(mbeanServer)},
* <code>namespace="<b>a</b>"</code>, and {@code mbean="b//c//D:k=v"})
* </li>
* <li id="check2">and in addition if namespace {@code "a"} is local,
* <code>JMXNamespacePermission(aSourceServerName,"Foo","<b>b//</b>c//D:k=v",
* "getAttribute")}</code>
* (where
* {@code aSourceServerName=MBeanServerFactory.getMBeanServerName(sourceServer(a))},
* <code>namespace="<b>b</b>"</code>, and {@code mbean="c//D:k=v"}),
* </li>
* <li id="check3">and in addition if namespace {@code "b"} is also local,
* <code>JMXNamespacePermission(bSourceServerName,"Foo","<b>c//</b>D:k=v",
* "getAttribute")}</code>
* (where
* {@code bSourceServerName=MBeanServerFactory.getMBeanServerName(sourceServer(b))},
* <code>namespace="<b>c</b>"</code>, and {@code mbean="D:k=v"}),
* </li>
* <li id="check4">and in addition if the source mbean server of namespace
* {@code "c"} is a also a local MBeanServer in this JVM,
* {@code MBeanPermission(cSourceServerName,<className(D:k=v)>,"Foo","D:k=v","getAttrinute")},
* (where
* {@code cSourceServerName=MBeanServerFactory.getMBeanServerName(sourceServer(c))}).
* </li>
* </ol>
* <p>For any of these MBean servers, if no name was supplied when
* creating that MBeanServer the {@link JMXNamespacePermission} is
* created with an {@code mbeanServerName} equal to
* {@value javax.management.MBeanServerFactory#DEFAULT_MBEANSERVER_NAME}.
* </p>
* <p>If the namespace {@code a} is in fact a remote {@code MBeanServer},
* for instance because namespace {@code a} is implemented by a {@link
* JMXRemoteNamespace} pointing to a distant MBeanServer located in
* another JMX agent, then checks <a href="#check2">2</a>,
* <a href="#check3">3</a>, and <a href="#check4">4</a> will not
* be performed in the local JVM. They might or might not be performed in
* the remote agent, depending on how access control and permission
* checking are configured in the remote agent, and how authentication
* is configured in the connector used by the {@link
* JMXRemoteNamespace}.
* </p>
* <p>In all cases, {@linkplain JMXNamespacePermission JMX Namespace Permissions}
* are checked as follows:</p>
* <p>First, if there is no security manager ({@link
* System#getSecurityManager()} is null), then an implementation of
* of MBeanServer that supports JMX namespaces is free not to make any
* checks.</p>
*
* <p>Assuming that there is a security manager, or that the
* implementation chooses to make checks anyway, the checks are made
* as detailed below.</p>
*
* <p>If a security check fails, the method throws {@link
* SecurityException}.</p>
*
* <ul>
*
* <li><p>For the {@link MBeanServer#invoke invoke} method, the caller's
* permissions must imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, &lt;operation name&gt;, &lt;namespace&gt;//&lt;mbean&gt;, "invoke")},
* where <a href="#MBeanServerName">mbean server name</a> is the name of the
* {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
* <a href="#NamespaceName">namespace</a> is registered, and
* <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
* is performed, in that namespace.
* </p>
*
* <li><p>For the {@link MBeanServer#getAttribute getAttribute} method, the
* caller's permissions must imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, &lt;attribute&gt;, &lt;namespace&gt;//&lt;mbean&gt;, "getAttribute")}.
* </p>
*
* <li><p>For the {@link MBeanServer#getAttributes getAttributes} method, the
* caller's permissions must imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, &lt;null&gt;, &lt;namespace&gt;//&lt;mbean&gt;, "getAttribute")},
* where <a href="#MBeanServerName">mbean server name</a> is the name of the
* {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
* <a href="#NamespaceName">namespace</a> is registered, and
* <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
* is performed, in that namespace.
* Additionally, for each attribute <em>att</em> in the {@link
* javax.management.AttributeList}, if the caller's permissions do not
* imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, <em>att</em>,
* &lt;namespace&gt;//&lt;mbean&gt;, "getAttribute")}, the
* MBean server will behave as if that attribute had not been in the
* supplied list.</p>
*
* <li><p>For the {@link MBeanServer#setAttribute setAttribute} method, the
* caller's permissions must imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, &lt;attrName&gt;, &lt;namespace&gt;//&lt;mbean&gt;, "setAttribute")},
* where <a href="#MBeanServerName">mbean server name</a> is the name of the
* {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
* <a href="#NamespaceName">namespace</a> is registered, and
* <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
* is performed, in that namespace, and
* <code>attrName</code> is {@link javax.management.Attribute#getName()
* attribute.getName()}.</p>
*
* <li><p>For the {@link MBeanServer#setAttributes setAttributes} method, the
* caller's permissions must imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;, "setAttribute")},
* where <a href="#MBeanServerName">mbean server name</a> is the name of the
* {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
* <a href="#NamespaceName">namespace</a> is registered, and
* <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
* is performed, in that namespace.
* Additionally, for each attribute <em>att</em> in the {@link
* javax.management.AttributeList}, if the caller's permissions do not
* imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, <em>att</em>, &lt;namespace&gt;//&lt;mbean&gt;, "setAttribute")},
* the MBean server will behave as if that attribute had not been in the
* supplied list.</p>
*
* <li><p>For the <code>addNotificationListener</code> methods,
* the caller's permissions must imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;,
* "addNotificationListener")},
* where <a href="#MBeanServerName">mbean server name</a> is the name of the
* {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
* <a href="#NamespaceName">namespace</a> is registered, and
* <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
* is performed, in that namespace.
* </p>
*
* <li><p>For the <code>removeNotificationListener</code> methods,
* the caller's permissions must imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;,
* "removeNotificationListener")},
* where <a href="#MBeanServerName">mbean server name</a> is the name of the
* {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
* <a href="#NamespaceName">namespace</a> is registered, and
* <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
* is performed, in that namespace.
* </p>
*
* <li><p>For the {@link MBeanServer#getMBeanInfo getMBeanInfo} method, the
* caller's permissions must imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;,
* "getMBeanInfo")},
* where <a href="#MBeanServerName">mbean server name</a> is the name of the
* {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
* <a href="#NamespaceName">namespace</a> is registered, and
* <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
* is performed, in that namespace.
* </p>
*
* <li><p>For the {@link MBeanServer#getObjectInstance getObjectInstance} method,
* the caller's permissions must imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;,
* "getObjectInstance")},
* where <a href="#MBeanServerName">mbean server name/a> is the name of the
* {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
* <a href="#NamespaceName">namespace</a> is registered, and
* <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
* is performed, in that namespace.
* </p>
*
* <li><p>For the {@link MBeanServer#isInstanceOf isInstanceOf} method, the
* caller's permissions must imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;,
* "isInstanceOf")},
* where <a href="#MBeanServerName">mbean server name</a> is the name of the
* {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
* <a href="#NamespaceName">namespace</a> is registered, and
* <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
* is performed, in that namespace.
* </p>
*
* <li><p>For the {@link MBeanServer#queryMBeans queryMBeans} method, the
* caller's permissions must imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, null, null,
* "queryMBeans")}.
* Additionally, for each MBean {@code mbean} that matches {@code pattern},
* if the caller's permissions do not imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;,
* "queryMBeans")}, the
* MBean server will behave as if that MBean did not exist.</p>
*
* <p>Certain query elements perform operations on the MBean server.
* However these operations are usually performed by the MBeanServer at the
* bottom of the namespace path, and therefore, do not involve any
* {@link JMXNamespacePermission} permission check. They might involve
* {@link javax.management.MBeanPermission} checks depending on how security
* in the JVM in which the bottom MBeanServer resides is implemented.
* See {@link javax.management.MBeanServer} for more details.
* </p>
*
* <li><p>For the {@link MBeanServer#queryNames queryNames} method, the checks
* are the same as for <code>queryMBeans</code> except that
* <code>"queryNames"</code> is used instead of
* <code>"queryMBeans"</code> in the <code>JMXNamespacePermission</code>
* objects. Note that a <code>"queryMBeans"</code> permission implies
* the corresponding <code>"queryNames"</code> permission.</p>
*
* <li><p>For the {@link MBeanServer#getClassLoader getClassLoader} method, the
* caller's permissions must imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;loaderName&gt;,
* "getClassLoader")},
* where <a href="#MBeanServerName">mbean server name/a> is the name of the
* {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
* <a href="#NamespaceName">namespace</a> is registered, and
* <a href="#MBeanName">loaderName</a> is the name of the ClassLoader MBean
* which is accessed, in that namespace.
* </p>
*
* <li><p>For the {@link MBeanServer#getClassLoaderFor getClassLoaderFor} method,
* the caller's permissions must imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;,
* "getClassLoaderFor")},
* where <a href="#MBeanServerName">mbean server name</a> is the name of the
* {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
* <a href="#NamespaceName">namespace</a> is registered, and
* <a href="#MBeanName">mbean</a> is the name of the MBean on which the action
* is performed, in that namespace.
* </p>
*
* <li><p>For the {@link MBeanServer#registerMBean registerMBean} method, the
* caller's permissions must imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, &lt;class name&gt;, &lt;namespace&gt;//&lt;mbean&gt;,
* "registerMBean")}. Here
* <code>class name</code> is the string returned by {@code
* obj.getClass().getName()} where {@code obj} is the mbean reference,
* <a href="#MBeanServerName"mbean server name/a> is the name of the
* {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
* <a href="#NamespaceName">namespace</a> is registered, and
* <a href="#MBeanName">mbean</a> is the name of the MBean which is being
* registered, relative to that namespace.
*
* <li><p>For the <code>createMBean</code> methods, the caller's
* permissions must imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, &lt;class name&gt;, &lt;namespace&gt;//&lt;mbean&gt;,
* "instantiate")} and
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, &lt;class name&gt;, &lt;namespace&gt;//&lt;mbean&gt;,
* "registerMBean")}, where
* <code>class name</code> is the string passed as first argument to the {@code
* createMBean} method,
* <a href="#MBeanServerName">mbean server name</a> is the name of the
* {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
* <a href="#NamespaceName">namespace</a> is registered, and
* <a href="#MBeanName">mbean</a> is the name of the MBean which is being
* created, relative to that namespace.
*
* <li><p>For the {@link MBeanServer#unregisterMBean unregisterMBean} method,
* the caller's permissions must imply {@link
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
* JMXNamespacePermission(&lt;mbean server name&gt;, null, &lt;namespace&gt;//&lt;mbean&gt;,
* "unregisterMBean")},
* where <a href="#MBeanServerName">mbean server name</a> is the name of the
* {@code MBeanServer} in which the {@code JMXNamespace} MBean in charge of
* <a href="#NamespaceName">namespace</a> is registered, and
* <a href="#MBeanName">mbean</a> is the name of the MBean on which is
* being unregistered, relative to that namespace.
* </p>
* </ul>
* </p>
* <p>It must be noted that if all namespaces are local, and all
* local namespaces are implemented by regular MBean servers, that is, there
* are no {@linkplain MBeanServerSupport Virtual Namespaces}, then
* simple {@linkplain javax.management.MBeanPermission MBean Permission}
* checks might be enough to secure an application.
* In that case, it is possible to specify the following {@link
* JMXNamespacePermission} permission in the policy file, which implies all
* other JMX namespace permissions:
* </p>
* <pre>
* permission javax.management.namespace.JMXNamespacePermission "*::*[]", "*";
* </pre>
*
* @since 1.7
*/
public class JMXNamespace
implements JMXNamespaceMBean, MBeanRegistration {
/**
* The standard value of the {@code type}
* property key that must be used to construct valid {@link
* JMXNamespaceMBean} ObjectNames.<br>
* This is {@value #TYPE}.
**/
public static final String TYPE = "JMXNamespace";
/**
* The {@link ObjectName#getKeyPropertyListString keyPropertyListString}
* that must be used to construct valid {@link JMXNamespaceMBean}
* ObjectNames.<br>
* This is
* <code>{@value #TYPE_ASSIGNMENT}</code>.
**/
public static final String TYPE_ASSIGNMENT = "type="+TYPE;
private volatile MBeanServer mbeanServer; // the mbean server in which
// this MBean is registered.
private volatile ObjectName objectName; // the ObjectName of this MBean.
private final MBeanServer sourceServer; // the MBeanServer within = the
// name space (or the MBean server
// that contains it).
private final String uuid;
/**
* Creates a new JMXNamespace implemented by means of an MBean Server.
* A namespace is equivalent to an MBeanServer within an MBean Server.
* The {@code sourceServer} provided to this constructor is the MBean Server
* within.
* @param sourceServer the MBean server that implemented by this namespace.
* @see #getSourceServer
*/
public JMXNamespace(MBeanServer sourceServer) {
this.sourceServer = sourceServer;
this.uuid = UUID.randomUUID().toString();
}
/**
* This method is part of the {@link MBeanRegistration} interface.
* The {@link JMXNamespace} class uses the {@link MBeanRegistration}
* interface in order to get a handle to the MBean server in which it is
* registered. It also check the validity of its own ObjectName.
* <p>
* This method is called by the MBean server.
* Application classes should never call this method directly.
* <p>
* If this method is overridden, the overriding method should call
* {@code super.preRegister(server,name)}.
* @see MBeanRegistration#preRegister MBeanRegistration
* @see JMXNamespaces#getNamespaceObjectName getNamespaceObjectName
* @param name The object name of the MBean. <var>name</var> must be a
* syntactically valid JMXNamespace name, as returned by
* {@link JMXNamespaces#getNamespaceObjectName(java.lang.String)
* getNamespaceObjectName(namespace)}.
* @return The name under which the MBean is to be registered.
* @throws IllegalArgumentException if the name supplied is not valid.
* @throws Exception can be thrown by subclasses.
*/
public ObjectName preRegister(MBeanServer server, ObjectName name)
throws Exception {
if (objectName != null && ! objectName.equals(name))
throw new IllegalStateException(
"Already registered under another name: " + objectName);
objectName = validateHandlerName(name);
mbeanServer = server;
return name;
}
/**
* Validate the ObjectName supplied to preRegister.
* This method is introduced to allow standard subclasses to use
* an alternate naming scheme. For instance - if we want to
* reuse JMXNamespace in order to implement sessions...
* It is however only available for subclasses in this package.
**/
ObjectName validateHandlerName(ObjectName supliedName) {
if (supliedName == null)
throw new IllegalArgumentException("Must supply a valid name");
final String dirName = JMXNamespaces.
normalizeNamespaceName(supliedName.getDomain());
final ObjectName handlerName =
JMXNamespaces.getNamespaceObjectName(dirName);
if (!supliedName.equals(handlerName))
throw new IllegalArgumentException("invalid name space name: "+
supliedName);
return supliedName;
}
/**
* This method is part of the {@link MBeanRegistration} interface.
* The {@link JMXNamespace} class uses the {@link MBeanRegistration}
* interface in order to get a handle to the MBean server in which it is
* registered.
* <p>
* This method is called by the MBean server. Application classes should
* not call this method directly. Subclasses are free to override this
* method with their own specific behavior - but the overriding method
* shoud still call {@code super.postRegister(registrationDone)}.
* @see MBeanRegistration#postRegister MBeanRegistration
*/
public void postRegister(Boolean registrationDone) {
// nothing to do
}
/**
* This method is part of the {@link MBeanRegistration} interface.
* The {@link JMXNamespace} class uses the {@link MBeanRegistration}
* interface in order to get a handle to the MBean server in which it is
* registered.
* <p>
* This method is called by the MBean server. Application classes should
* not call this method directly. Subclasses are free to override this
* method with their own specific behavior - but the overriding method
* shoud still call {@code super.preDeregister()}.
* @see MBeanRegistration#preDeregister MBeanRegistration
*/
public void preDeregister() throws Exception {
// nothing to do
}
/**
* This method is part of the {@link MBeanRegistration} interface.
* It allows the {@code JMXNamespace} MBean to perform any operations
* needed after having been unregistered in the MBean server.
* <p>
* This method is called by the MBean server. Application classes should
* not call this method directly. If a subclass overrides this
* method, the overriding method shoud call {@code super.postDeregister()}.
* @see MBeanRegistration#postDeregister MBeanRegistration
*/
public void postDeregister() {
mbeanServer = null;
objectName = null;
}
/**
* Returns the MBeanServer in which this MBean is registered,
* or null. Chiefly of interest for subclasses.
* @return the MBeanServer supplied to {@link #preRegister}.
**/
public final MBeanServer getMBeanServer() {
return mbeanServer;
}
/**
* Returns the MBeanServer that contains or emulates the source
* namespace. When a JMXNamespace MBean is registered in an
* MBean server created through the default {@link
* javax.management.MBeanServerBuilder}, the MBeanServer will
* check {@link JMXNamespacePermission} before invoking
* any method on the source MBeanServer of the JMXNamespace.
* See <a href="#PermissionChecks">JMX Namespace Permission Checks</a>
* above.
* @return an MBeanServer view of the source namespace
**/
public MBeanServer getSourceServer() {
return sourceServer;
}
/**
* Returns the ObjectName with which this MBean was registered,
* or null. Chiefly of interest for subclasses.
* @return the ObjectName supplied to {@link #preRegister}.
**/
public final ObjectName getObjectName() {
return objectName;
}
/**
* HandlerName used in traces.
**/
String getHandlerName() {
final ObjectName name = getObjectName();
if (name != null) return name.toString();
return this.toString();
}
/**
* In this class, this method returns {@link #getSourceServer
* getSourceServer()}.{@link javax.management.MBeanServer#getMBeanCount
* getMBeanCount()}.
* <br>This default behaviour may be redefined in subclasses.
* @throws java.io.IOException can be thrown by subclasses.
*/
public Integer getMBeanCount() throws IOException {
return getSourceServer().getMBeanCount();
}
/**
* In this class, this method returns {@link #getSourceServer
* getSourceServer()}.{@link javax.management.MBeanServer#getDomains
* getDomains()}.
* <br>This default behaviour may be redefined in subclasses.
* @throws java.io.IOException can be thrown by subclasses.
*/
public String[] getDomains() throws IOException {
return getSourceServer().getDomains();
}
/**
* In this class, this method returns {@link #getSourceServer
* getSourceServer()}.{@link javax.management.MBeanServer#getDefaultDomain
* getDefaultDomain()}.
* <br>This default behaviour may be redefined in subclasses.
* @throws java.io.IOException can be thrown by subclasses.
*/
public String getDefaultDomain() throws IOException {
return getSourceServer().getDefaultDomain();
}
public final String getUUID() {
return uuid;
}
}

View file

@ -0,0 +1,96 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package javax.management.namespace;
import java.io.IOException;
import java.util.UUID;
/**
* A {@link JMXNamespace} is an MBean that handles a name space in the
* MBeanServer hierarchical name space.
* @see JMXNamespace
* @since 1.7
*/
public interface JMXNamespaceMBean {
// Note: since getDomains(), getDefaultDomain(), and getMBeanCount()
// don't take any ObjectName parameters, the only efficient way
// to get these data is to call the corresponding method on the
// JMXNamespaceMBean that handles the name space.
//
// We need these methods to implement 'cd' (JMXNamespaces.narrowToNamespace)
// correctly.
//
/**
* Returns the list of domains currently implemented in the name space
* handled by this {@link JMXNamespace}.
* @throws IOException if the domain list cannot be obtained due to
* I/O problems (communication failures etc...).
* @return the list of domains currently implemented in the name space
* handled by this {@link JMXNamespace}.
* @see javax.management.MBeanServerConnection#getDomains
* MBeanServerConnection.getDomains
**/
public String[] getDomains() throws IOException;
/**
* Returns the default domain for the name space handled by
* this {@link JMXNamespace}.
* @throws IOException if the default domain cannot be obtained due to
* I/O problems (communication failures etc...).
* @return the default domain for the name space handled by
* this {@link JMXNamespace}.
* @see javax.management.MBeanServerConnection#getDefaultDomain
* MBeanServerConnection.getDefaultDomain
**/
public String getDefaultDomain() throws IOException;
/**
* Returns the number of MBeans registered in the name space handled by
* this {@link JMXNamespace}.
*
* @return the number of MBeans registered in the name space handled by
* this {@link JMXNamespace}.
*
* @throws IOException if the MBean count cannot be obtained due to
* I/O problems (communication failures etc...).
* @see javax.management.MBeanServerConnection#getMBeanCount
* MBeanServerConnection.getMBeanCount
*/
public Integer getMBeanCount() throws IOException;
/**
* Returns a {@link java.util.UUID UUID string} which uniquely identifies
* this {@linkplain JMXNamespace} MBean.
* This information can be used to detect loops in the JMX name space graph.
* @return A unique ID identifying this MBean.
* @throws IOException if the MBean UUID cannot be obtained due to
* I/O problems (communication failures etc...).
*/
public String getUUID() throws IOException;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,300 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package javax.management.namespace;
import java.io.IOException;
import java.util.Set;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
/**
* This class makes it possible to navigate easily within a hierarchical
* namespace view.
*
* <pre>
* MBeanServerConnnection rootConnection = ...;
*
* // create a view at the local root of the namespace hierarchy.
* //
* JMXNamespaceView view = new JMXNamespaceView(rootConnection);
*
* // list all top level namespaces
* String[] list = view.list();
*
* // select one namespace from the list
* String whereToGo = ... ;
*
* // go down to the selected namespace:
* view = view.down(whereToGo);
* System.out.println("I am now in: " + view.where());
* System.out.println("I can see these MBeans:" +
* view.getMBeanServerConnection().queryNames(null,null));
*
* // list sub namespaces in current view ('whereToGo')
* list = view.list();
* System.out.println("Here are the sub namespaces of "+view.where()+": "+
* Arrays.toString(list));
*
* // go up one level
* view = view.up();
* System.out.println("I am now back to: " +
* (view.isRoot() ? "root namespace" : view.where()));
* </pre>
* @since 1.7
*/
public class JMXNamespaceView {
private static final ObjectName ALL_NAMESPACES;
static {
try {
ALL_NAMESPACES = ObjectName.getInstance("*" +
JMXNamespaces.NAMESPACE_SEPARATOR + ":"+
JMXNamespace.TYPE_ASSIGNMENT);
} catch (MalformedObjectNameException x) {
throw new ExceptionInInitializerError(x);
}
}
private static final int NAMESPACE_SEPARATOR_LENGTH =
JMXNamespaces.NAMESPACE_SEPARATOR.length();
private final JMXNamespaceView parent;
private final MBeanServerConnection here;
private final String where;
private static MBeanServerConnection checkRoot(MBeanServerConnection root) {
if (root == null)
throw new IllegalArgumentException(
"namespaceRoot: null is not a valid value");
return root;
}
/**
* Creates a view at the top of a JMX namespace hierarchy.
* @param namespaceRoot The {@code MBeanServerConnection} at the
* top of the hierarchy.
*/
public JMXNamespaceView(MBeanServerConnection namespaceRoot) {
this(null,checkRoot(namespaceRoot),"");
}
// This constructor should remain private. A user can only create
// JMXNamespaceView at the top of the hierarchy.
// JMXNamespaceView sub nodes are created by their parent nodes.
private JMXNamespaceView(JMXNamespaceView parent,
MBeanServerConnection here, String where) {
this.parent = parent;
this.here = here;
this.where = where;
}
/**
* Returns the path leading to the namespace in this view, from
* the top of the hierarchy.
* @return The path to the namespace in this view.
*/
public String where() {
return where;
}
/**
* Lists all direct sub namespaces in this view. The returned strings
* do not contain the {@code //} separator.
*
* @return A list of direct sub name spaces accessible from this
* namespace.
* @throws IOException if the attempt to list the namespaces fails because
* of a communication problem.
*/
public String[] list() throws IOException {
final Set<ObjectName> names =
here.queryNames(ALL_NAMESPACES,null);
final String[] res = new String[names.size()];
int i = 0;
for (ObjectName dirName : names) {
final String dir = dirName.getDomain();
res[i++]=dir.substring(0,dir.length()-NAMESPACE_SEPARATOR_LENGTH);
}
return res;
}
/**
* Go down into a sub namespace.
* @param namespace the namespace to go down to. It can contain one or
* more {@code //} separators, to traverse intermediate namespaces, but
* it must not begin or end with {@code //} or contain an empty
* intermediate namespace. If it is the empty string, then {@code this} is
* returned.
* @return A view of the named sub namespace.
* @throws IllegalArgumentException if the {@code namespace} begins or
* ends with {@code //}.
*/
public JMXNamespaceView down(String namespace) {
if (namespace.equals("")) return this;
if (namespace.startsWith(JMXNamespaces.NAMESPACE_SEPARATOR))
throw new IllegalArgumentException(namespace+": can't start with "+
JMXNamespaces.NAMESPACE_SEPARATOR);
// This is a convenience to handle paths like xxx//yyy
final String[] elts =
namespace.split(JMXNamespaces.NAMESPACE_SEPARATOR);
// Go down the path, creating all sub namespaces along the way.
// Usually there will be a single element in the given namespace
// name, but we don't want to forbid things like
// down("xxx//yyy/www");
//
JMXNamespaceView previous = this;
String cursor = where;
for (String elt : elts) {
// empty path elements are not allowed. It means we
// had something like "xxx////yyy"
if (elt.equals(""))
throw new IllegalArgumentException(namespace+
": invalid path element");
// compute the "where" for the child.
cursor = JMXNamespaces.concat(cursor, elt);
// create the child...
final JMXNamespaceView next =
makeJMXNamespaceView(root(), previous, cursor);
// the current child will be the parent of the next child...
previous = next;
}
// We return the last child that was created.
return previous;
}
/**
* Go back up one level. If this view is at the root of the
* hierarchy, returns {@code null}.
* @return A view of the parent namespace, or {@code null} if we're at
* the root of the hierarchy.
*/
public JMXNamespaceView up() {
return parent;
}
/**
* Tells whether this view is at the root of the hierarchy.
* @return {@code true} if this view is at the root of the hierachy.
*/
public boolean isRoot() {
return parent == null;
}
/**
* Returns the view at the root of the hierarchy.
* If we are already at the root, this is {@code this}.
* @return the view at the root of the hierarchy.
*/
public JMXNamespaceView root() {
if (parent == null) return this;
return parent.root();
}
/**
* A MBeanServerConnection to the namespace shown by this view.
* This is what would have been obtained by doing:
* <pre>
* JMX.narrowToNamespace(this.root().getMBeanServerConnection(),
* this.where());
* </pre>
* @return A MBeanServerConnection to the namespace shown by this view.
*/
public MBeanServerConnection getMBeanServerConnection() {
return here;
}
/**
* <p>Get the name of the JMXNamespaceMBean handling the namespace shown by
* this view, relative to the root of the hierarchy. If we are at the root
* of the hierarchy, this method returns {@code null}.</p>
*
* <p>You can use this method to make a proxy for the JMXNamespaceMBean
* as follows:</p>
*
* <pre>
* JMXNamespaceView view = ...;
* ObjectName namespaceMBeanName = view.getJMXNamespaceMBeanName();
* JMXNamespaceMBean namespaceMBean = JMX.newMBeanProxy(
* view.root().getMBeanServerConnection(), namespaceMBeanName,
* JMXNamespaceMBean.class);
* </pre>
*
* @return The name of the {@code JMXNamespaceMBean} handling the namespace
* shown by this view, or {@code null}.
*/
public ObjectName getJMXNamespaceMBeanName() {
if (parent == null)
return null;
else
return JMXNamespaces.getNamespaceObjectName(where);
}
@Override
public int hashCode() {
return where.hashCode();
}
/**
* Returns true if this object is equal to the given object. The
* two objects are equal if the other object is also a {@code
* JMXNamespaceView} and both objects have the same {@linkplain #root root}
* MBeanServerConnection and the same {@linkplain #where path}.
* @param o the other object to compare to.
* @return true if both objects are equal.
*/
@Override
public boolean equals(Object o) {
if (o==this) return true;
if (! (o instanceof JMXNamespaceView)) return false;
if (!where.equals(((JMXNamespaceView)o).where)) return false;
return root().getMBeanServerConnection().equals(
((JMXNamespaceView)o).root().getMBeanServerConnection());
}
private JMXNamespaceView makeJMXNamespaceView(final JMXNamespaceView root,
final JMXNamespaceView directParent, final String pathFromRoot) {
if (pathFromRoot.equals("")) return root;
return new JMXNamespaceView(directParent,
narrowToNamespace(root.getMBeanServerConnection(),
pathFromRoot),pathFromRoot);
}
private MBeanServerConnection narrowToNamespace(MBeanServerConnection root,
String path) {
if (root instanceof MBeanServer)
return JMXNamespaces.narrowToNamespace((MBeanServer)root, path);
return JMXNamespaces.narrowToNamespace(root, path);
}
}

View file

@ -0,0 +1,374 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package javax.management.namespace;
import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.namespace.JMXNamespaceUtils;
import com.sun.jmx.namespace.ObjectNameRouter;
import com.sun.jmx.namespace.serial.RewritingProcessor;
import com.sun.jmx.namespace.RoutingConnectionProxy;
import com.sun.jmx.namespace.RoutingServerProxy;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
/**
* Static constants and utility methods to help work with
* JMX name spaces. There are no instances of this class.
* @since 1.7
*/
public class JMXNamespaces {
/**
* A logger for this class.
**/
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
/** Creates a new instance of JMXNamespaces */
private JMXNamespaces() {
}
/**
* The name space separator. This is an alias for {@link
* ObjectName#NAMESPACE_SEPARATOR}.
**/
public static final String NAMESPACE_SEPARATOR =
ObjectName.NAMESPACE_SEPARATOR;
private static final int NAMESPACE_SEPARATOR_LENGTH =
NAMESPACE_SEPARATOR.length();
/**
* Returns a connector connected to a sub name space exposed through
* the parent connector.
* @param parent the parent connector.
* @param namespace the {@linkplain javax.management.namespace name space}
* to which the returned connector is
* connected.
* @return A connector connected to a sub name space exposed through
* the parent connector.
**/
public static JMXConnector narrowToNamespace(final JMXConnector parent,
final String namespace)
throws IOException {
return JMXNamespaceUtils.cd(parent,namespace,true);
}
/**
* Creates a new {@code MBeanServerConnection} proxy on a
* {@linkplain javax.management.namespace sub name space}
* of the given parent.
*
* @param parent The parent {@code MBeanServerConnection} that contains
* the name space.
* @param namespace The {@linkplain javax.management.namespace
* name space} in which to narrow.
* @return A new {@code MBeanServerConnection} proxy that shows the content
* of that name space.
* @throws IllegalArgumentException if the name space does not exist, or
* if a proxy for that name space cannot be created.
*/
public static MBeanServerConnection narrowToNamespace(
MBeanServerConnection parent,
String namespace) {
if (LOG.isLoggable(Level.FINER))
LOG.finer("Making MBeanServerConnection for: " +namespace);
return RoutingConnectionProxy.cd(parent,namespace);
}
/**
* Creates a new {@code MBeanServer} proxy on a
* {@linkplain javax.management.namespace sub name space}
* of the given parent.
*
* @param parent The parent {@code MBeanServer} that contains
* the name space.
* @param namespace The {@linkplain javax.management.namespace
* name space} in which to narrow.
* @return A new {@code MBeanServer} proxy that shows the content
* of that name space.
* @throws IllegalArgumentException if either argument is null,
* or the name space does not exist, or if a proxy for that name space
* cannot be created.
*/
public static MBeanServer narrowToNamespace(MBeanServer parent,
String namespace) {
if (LOG.isLoggable(Level.FINER))
LOG.finer("Making NamespaceServerProxy for: " +namespace);
return RoutingServerProxy.cd(parent,namespace);
}
/**
* Returns an object that is the same as the given object except that
* any {@link ObjectName} it might contain has its domain modified.
* The returned object might be identical to the given object if it
* does not contain any {@code ObjectName} values or if none of them
* were modified.
* This method will replace a prefix ({@code toRemove}) from the path of
* the ObjectNames contained in {@code obj} by another prefix
* ({@code toAdd}).
* Therefore, all contained ObjectNames must have a path that start with
* the given {@code toRemove} prefix. If one of them doesn't, an {@link
* IllegalArgumentException} is thrown.
* <p>
* For instance, if {@code obj} contains the ObjectName
* {@code x//y//z//d:k=x}, and {@code toAdd} is {@code v//w}, and
* {@code toRemove}
* is {@code x//y} this method will return a copy of {@code obj} that
* contains {@code v//w//z//d:k=x}.<br>
* On the other hand, if {@code obj} contains the ObjectName
* {@code x//y//z//d:k=x}, and {@code toAdd} is {@code v//w}, and
* {@code toRemove} is {@code v} this method
* will raise an exception, because {@code x//y//z//d:k=x} doesn't start
* with {@code v}
* </p>
* <p>Note: the default implementation of this method can use the
* Java serialization framework to clone and replace ObjectNames in the
* provided {@code obj}. It will usually fail if {@code obj} is not
* Java serializable, or contains objects which are not Java
* serializable.
* </p>
* @param obj The object to deep-rewrite
* @param toRemove a prefix already present in contained ObjectNames.
* If {@code toRemove} is the empty string {@code ""}, nothing
* will be removed from the contained ObjectNames.
* @param toAdd the prefix that will replace (@code toRemove} in contained
* ObjectNames.
* If {@code toAdd} is the empty string {@code ""}, nothing
* will be added to the contained ObjectNames.
* @return the rewritten object, or possibly {@code obj} if nothing needed
* to be changed.
* @throws IllegalArgumentException if {@code obj} couldn't be rewritten or
* if {@code toRemove} or {@code toAdd} is null.
**/
public static <T> T deepReplaceHeadNamespace(T obj, String toRemove, String toAdd) {
final RewritingProcessor processor =
RewritingProcessor.newRewritingProcessor(toAdd,toRemove);
return processor.rewriteOutput(obj);
}
/**
* Appends {@code namespace} to {@code path}.
* This methods appends {@code namespace} to {@code path} to obtain a
* a <i>full path</i>, and normalizes the result thus obtained:
* <ul>
* <li>If {@code path} is empty, the full path is
* {@code namespace}.</li>
* <li>Otherwise, if {@code namespace} is empty,
* the full path is {@code path}</li>
* <li>Otherwise, and this is the regular case, the full path is the
* result of the concatenation of
* {@code path}+{@value #NAMESPACE_SEPARATOR}+{@code namespace}</li>
* <li>finally, the full path is normalized: multiple consecutive
* occurrences of {@value #NAMESPACE_SEPARATOR} are replaced by a
* single {@value #NAMESPACE_SEPARATOR} in the result, and trailing
* occurences of {@value #NAMESPACE_SEPARATOR} are removed.
* </li>
* </ul>
* @param path a name space path prefix
* @param namespace a name space name to append to the path
* @return a syntactically valid name space path, or "" if both parameters
* are null or empty.
* @throws IllegalArgumentException if either argument is null or ends with
* an odd number of {@code /} characters.
**/
public static String concat(String path, String namespace) {
if (path == null || namespace == null)
throw new IllegalArgumentException("Null argument");
checkTrailingSlashes(path);
checkTrailingSlashes(namespace);
final String result;
if (path.equals("")) result=namespace;
else if (namespace.equals("")) result=path;
else result=path+NAMESPACE_SEPARATOR+namespace;
return ObjectNameRouter.normalizeNamespacePath(result,false,true,false);
}
/**
* Returns a syntactically valid name space path.
* If the provided {@code namespace} ends with {@code "//"},
* recursively strips trailing {@code "//"}. Each sequence of an
* even number of {@code "/"} characters is also replaced by {@code "//"},
* for example {@code "foo//bar////baz/////buh"} will become
* {@code "foo//bar//baz///buh"}.
*
* @param namespace A name space path
* @return {@code ""} - if the provided {@code namespace} resolves to
* the empty string; otherwise a syntactically valid name space string
* stripped of trailing and redundant {@code "//"}.
* @throws IllegalArgumentException if {@code namespace} is null or
* is not syntactically valid (e.g. it contains
* invalid characters like ':', or it ends with an odd
* number of '/').
*/
public static String normalizeNamespaceName(String namespace) {
if (namespace == null)
throw new IllegalArgumentException("Null namespace");
final String sourcePath =
ObjectNameRouter.normalizeNamespacePath(namespace,false,true,false);
if (sourcePath.equals("")) return sourcePath;
// Will throw an IllegalArgumentException if the namespace name
// is not syntactically valid...
//
getNamespaceObjectName(sourcePath);
return sourcePath;
}
/**
* Return a canonical handler name for the provided {@code namespace},
* The handler name returned will be
* {@link #normalizeNamespaceName normalizeNamespaceName}{@code (namespace) +
* "//:type=JMXNamespace"}.
*
* @param namespace A name space path
* @return a canonical ObjectName for a name space handler.
* @see #normalizeNamespaceName
* @throws IllegalArgumentException if the provided
* {@code namespace} is null or not valid.
*/
public static ObjectName getNamespaceObjectName(String namespace) {
if (namespace == null || namespace.equals(""))
throw new IllegalArgumentException("Null or empty namespace");
final String sourcePath =
ObjectNameRouter.normalizeNamespacePath(namespace,false,
true,false);
try {
return ObjectName.getInstance(sourcePath+
NAMESPACE_SEPARATOR+":"+
JMXNamespace.TYPE_ASSIGNMENT);
} catch (MalformedObjectNameException x) {
throw new IllegalArgumentException(namespace,x);
}
}
/**
* Returns an ObjectName pattern that can be used to query for all MBeans
* contained in the given name space.
* For instance, if {@code namespace="foo//bar"}, this method will
* return {@code "foo//bar//*:*"}
* @return an ObjectName pattern that selects all MBeans in the given
* name space.
**/
public static ObjectName getWildcardFor(String namespace) {
return insertPath(namespace,ObjectName.WILDCARD);
}
/**
* Returns an ObjectName that can be used to access an MBean
* contained in the given name space.
* For instance, if {@code path="foo//bar"}, and
* {@code to="domain:type=Thing"} this method will
* return {@code "foo//bar//domain:type=Thing"}
* @return an ObjectName that can be used to invoke an MBean located in a
* sub name space.
* @throws IllegalArgumentException if {@code path} ends with an
* odd number of {@code /} characters.
**/
public static ObjectName insertPath(String path, ObjectName to) {
if (path == null || to == null)
throw new IllegalArgumentException("Null argument");
checkTrailingSlashes(path);
try {
String prefix = path;
if (!prefix.equals("")) prefix =
ObjectNameRouter.normalizeNamespacePath(
prefix + NAMESPACE_SEPARATOR,false,false,false);
return to.withDomain(
ObjectNameRouter.normalizeDomain(
prefix+to.getDomain(),false));
} catch (MalformedObjectNameException x) {
throw new IllegalArgumentException(path+": "+x,x);
}
}
/**
* Returns the normalized name space path of the name space expected to
* contain {@code ObjectName}.
* For instance, for {@code "foo//domain:type=Thing"} this will be
* {@code "foo"}. For {@code "//foo//bar//domain:type=Thing"} this will be
* {@code "foo//bar"}. For {@code //foo//bar//baz//domain:type=Thing}
* this will be {@code "foo//bar//baz"}. For
* {@code //foo//bar//baz//:type=JMXNamespace}
* this will be {@code "foo//bar"}.
*
* @param name an {@code ObjectName}
* @return the name space path of the name space that could contain such
* a name. If {@code name} has no name space, returns {@code ""}.
* @throws IllegalArgumentException if {@code name} is null.
**/
public static String getContainingNamespace(ObjectName name) {
return getNormalizedPath(name,true);
}
static String getNormalizedPath(ObjectName name,
boolean removeLeadingSep) {
if (name == null)
throw new IllegalArgumentException("Null name");
String domain =
ObjectNameRouter.normalizeDomain(name.getDomain(),removeLeadingSep);
int end = domain.length();
// special case of domain part being a single '/'
//
if (domain.endsWith(NAMESPACE_SEPARATOR+"/"))
return domain.substring(0,end-NAMESPACE_SEPARATOR_LENGTH-1);
// special case of namespace handler
//
if (domain.endsWith(NAMESPACE_SEPARATOR))
domain = domain.substring(0,end-NAMESPACE_SEPARATOR_LENGTH);
int last = domain.lastIndexOf(NAMESPACE_SEPARATOR);
if (last < 0) return "";
if (last == 0) return domain;
// special case of domain part starting with '/'
// last=0 is not possible - we took care of this above.
if (domain.charAt(last-1) == '/') last--;
return domain.substring(0,last);
}
private static void checkTrailingSlashes(String path) {
int i;
for (i = path.length() - 1; i >= 0 && path.charAt(i) == '/'; i--)
continue;
if (path.length() - i % 2 == 0)
throw new IllegalArgumentException("Path ends with odd number of /");
}
}

View file

@ -0,0 +1,837 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package javax.management.namespace;
import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.mbeanserver.Util;
import com.sun.jmx.namespace.JMXNamespaceUtils;
import com.sun.jmx.namespace.NamespaceInterceptor.DynamicProbe;
import com.sun.jmx.remote.util.EnvHelp;
import java.io.IOException;
import java.security.AccessControlException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.AttributeChangeNotification;
import javax.management.InstanceNotFoundException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanPermission;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.event.EventClient;
import javax.management.remote.JMXConnectionNotification;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
/**
* A {@link JMXNamespace} that will connect to a remote MBeanServer
* by creating a {@link javax.management.remote.JMXConnector} from a
* {@link javax.management.remote.JMXServiceURL}.
* <p>
* You can call {@link #connect() connect()} and {@link #close close()}
* several times. This MBean will emit an {@link AttributeChangeNotification}
* when the value of its {@link #isConnected Connected} attribute changes.
* </p>
* <p>
* The JMX Remote Namespace MBean is not connected until {@link
* #connect() connect()} is explicitly called. The usual sequence of code to
* create a JMX Remote Namespace is thus:
* </p>
* <pre>
* final String namespace = "mynamespace";
* final ObjectName name = {@link JMXNamespaces#getNamespaceObjectName
* JMXNamespaces.getNamespaceObjectName(namespace)};
* final JMXServiceURL remoteServerURL = .... ;
* final Map<String,Object> optionsMap = .... ;
* final MBeanServer masterMBeanServer = .... ;
* final JMXRemoteNamespace namespaceMBean = {@link #newJMXRemoteNamespace
* JMXRemoteNamespace.newJMXRemoteNamespace(remoteServerURL, optionsMap)};
* masterMBeanServer.registerMBean(namespaceMBean, name);
* namespaceMBean.connect();
* // or: masterMBeanServer.invoke(name, {@link #connect() "connect"}, null, null);
* </pre>
* <p>
* The JMX Remote Namespace MBean will register for {@linkplain
* JMXConnectionNotification JMX Connection Notifications} with its underlying
* {@link JMXConnector}. When a JMX Connection Notification indicates that
* the underlying connection has failed, the JMX Remote Namespace MBean
* closes its underlying connector and switches its {@link #isConnected
* Connected} attribute to false, emitting an {@link
* AttributeChangeNotification}.
* </p>
* <p>
* At this point, a managing application (or an administrator connected
* through a management console) can attempt to reconnect the
* JMX Remote Namespace MBean by calling its {@link #connect() connect()} method
* again.
* </p>
* <p>Note that when the connection with the remote namespace fails, or when
* {@link #close} is called, then any notification subscription to
* MBeans registered in that namespace will be lost - unless a custom
* {@linkplain javax.management.event event service} supporting connection-less
* mode was used.
* </p>
* @since 1.7
*/
public class JMXRemoteNamespace
extends JMXNamespace
implements JMXRemoteNamespaceMBean, NotificationEmitter {
/**
* A logger for this class.
*/
private static final Logger LOG = JmxProperties.NAMESPACE_LOGGER;
private static final Logger PROBE_LOG = Logger.getLogger(
JmxProperties.NAMESPACE_LOGGER_NAME+".probe");
// This connection listener is used to listen for connection events from
// the underlying JMXConnector. It is used in particular to maintain the
// "connected" state in this MBean.
//
private static class ConnectionListener implements NotificationListener {
private final JMXRemoteNamespace handler;
private ConnectionListener(JMXRemoteNamespace handler) {
this.handler = handler;
}
public void handleNotification(Notification notification,
Object handback) {
if (!(notification instanceof JMXConnectionNotification))
return;
final JMXConnectionNotification cn =
(JMXConnectionNotification)notification;
handler.checkState(this,cn,(JMXConnector)handback);
}
}
// When the JMXRemoteNamespace is originally created, it is not connected,
// which means that the source MBeanServer should be one that throws
// exceptions for most methods. When it is subsequently connected,
// the methods should be forwarded to the MBeanServerConnection.
// We handle this using MBeanServerConnectionWrapper. The
// MBeanServerConnection that is supplied to the constructor of
// MBeanServerConnectionWrapper is ignored (and in fact it is null)
// because the one that is actually used is the one supplied by the
// override of getMBeanServerConnection().
private static class JMXRemoteNamespaceDelegate
extends MBeanServerConnectionWrapper
implements DynamicProbe {
private volatile JMXRemoteNamespace parent=null;
JMXRemoteNamespaceDelegate() {
super(null,null);
}
@Override
public MBeanServerConnection getMBeanServerConnection() {
return parent.getMBeanServerConnection();
}
@Override
public ClassLoader getDefaultClassLoader() {
return parent.getDefaultClassLoader();
}
// Because this class is instantiated in the super() call from the
// constructor of JMXRemoteNamespace, it cannot be an inner class.
// This method achieves the effect that an inner class would have
// had, of giving the class a reference to the outer "this".
synchronized void initParentOnce(JMXRemoteNamespace parent) {
if (this.parent != null)
throw new UnsupportedOperationException("parent already set");
this.parent=parent;
}
public boolean isProbeRequested() {
return this.parent.isProbeRequested();
}
}
private static final MBeanNotificationInfo connectNotification =
new MBeanNotificationInfo(new String[] {
AttributeChangeNotification.ATTRIBUTE_CHANGE},
"Connected",
"Emitted when the Connected state of this object changes");
private static long seqNumber=0;
private final NotificationBroadcasterSupport broadcaster;
private final ConnectionListener listener;
private final JMXServiceURL jmxURL;
private final Map<String,?> optionsMap;
private volatile MBeanServerConnection server = null;
private volatile JMXConnector conn = null;
private volatile ClassLoader defaultClassLoader = null;
private volatile boolean probed;
/**
* Creates a new instance of {@code JMXRemoteNamespace}.
* <p>
* This constructor is provided for subclasses.
* To create a new instance of {@code JMXRemoteNamespace} call
* {@link #newJMXRemoteNamespace
* JMXRemoteNamespace.newJMXRemoteNamespace(sourceURL, optionsMap)}.
* </p>
* @param sourceURL a JMX service URL that can be used to {@linkplain
* #connect() connect} to the
* source MBean Server. The source MBean Server is the remote
* MBean Server which contains the MBeans that will be mirrored
* in this namespace.
* @param optionsMap the options map that will be passed to the
* {@link JMXConnectorFactory} when {@linkplain
* JMXConnectorFactory#newJMXConnector creating} the
* {@link JMXConnector} used to {@linkplain #connect() connect}
* to the remote source MBean Server. Can be null, which is
* equivalent to an empty map.
* @see #newJMXRemoteNamespace JMXRemoteNamespace.newJMXRemoteNamespace
* @see #connect
*/
protected JMXRemoteNamespace(JMXServiceURL sourceURL,
Map<String,?> optionsMap) {
super(new JMXRemoteNamespaceDelegate());
((JMXRemoteNamespaceDelegate)super.getSourceServer()).
initParentOnce(this);
// URL must not be null.
this.jmxURL = JMXNamespaceUtils.checkNonNull(sourceURL,"url");
this.broadcaster =
new NotificationBroadcasterSupport(connectNotification);
// handles options
this.optionsMap = JMXNamespaceUtils.unmodifiableMap(optionsMap);
// handles (dis)connection events
this.listener = new ConnectionListener(this);
// XXX TODO: remove the probe, or simplify it.
this.probed = false;
}
/**
* Returns the {@code JMXServiceURL} that is (or will be) used to
* connect to the remote name space. <p>
* @see #connect
* @return The {@code JMXServiceURL} used to connect to the remote
* name space.
*/
public JMXServiceURL getJMXServiceURL() {
return jmxURL;
}
/**
* In this class, this method never returns {@code null}, and the
* address returned is the {@link #getJMXServiceURL JMXServiceURL}
* that is used by this object to {@linkplain #connect} to the remote
* name space. <p>
* This behaviour might be overriden by subclasses, if needed.
* For instance, a subclass might want to return {@code null} if it
* doesn't want to expose that JMXServiceURL.
*/
public JMXServiceURL getAddress() {
return getJMXServiceURL();
}
private Map<String,?> getEnvMap() {
return optionsMap;
}
boolean isProbeRequested() {
return probed==false;
}
public void addNotificationListener(NotificationListener listener,
NotificationFilter filter, Object handback) {
broadcaster.addNotificationListener(listener, filter, handback);
}
/**
* A subclass that needs to send its own notifications must override
* this method in order to return an {@link MBeanNotificationInfo
* MBeanNotificationInfo[]} array containing both its own notification
* infos and the notification infos of its super class. <p>
* The implementation should probably look like:
* <pre>
* final MBeanNotificationInfo[] myOwnNotifs = { .... };
* final MBeanNotificationInfo[] parentNotifs =
* super.getNotificationInfo();
* final Set<MBeanNotificationInfo> mergedResult =
* new HashSet<MBeanNotificationInfo>();
* mergedResult.addAll(Arrays.asList(myOwnNotifs));
* mergedResult.addAll(Arrays.asList(parentNotifs));
* return mergeResult.toArray(
* new MBeanNotificationInfo[mergedResult.size()]);
* </pre>
*/
public MBeanNotificationInfo[] getNotificationInfo() {
return broadcaster.getNotificationInfo();
}
public void removeNotificationListener(NotificationListener listener)
throws ListenerNotFoundException {
broadcaster.removeNotificationListener(listener);
}
public void removeNotificationListener(NotificationListener listener,
NotificationFilter filter, Object handback)
throws ListenerNotFoundException {
broadcaster.removeNotificationListener(listener, filter, handback);
}
private static synchronized long getNextSeqNumber() {
return seqNumber++;
}
/**
* Sends a notification to registered listeners. Before the notification
* is sent, the following steps are performed:
* <ul><li>
* If {@code n.getSequenceNumber() <= 0} set it to the next available
* sequence number.</li>
* <li>If {@code n.getSource() == null}, set it to the value returned by {@link
* #getObjectName getObjectName()}.
* </li></ul>
* <p>This method can be called by subclasses in order to send their own
* notifications.
* In that case, these subclasses might also need to override
* {@link #getNotificationInfo} in order to declare their own
* {@linkplain MBeanNotificationInfo notification types}.
* </p>
* @param n The notification to send to registered listeners.
* @see javax.management.NotificationBroadcasterSupport
* @see #getNotificationInfo
**/
protected void sendNotification(Notification n) {
if (n.getSequenceNumber()<=0)
n.setSequenceNumber(getNextSeqNumber());
if (n.getSource()==null)
n.setSource(getObjectName());
broadcaster.sendNotification(n);
}
private void checkState(ConnectionListener listener,
JMXConnectionNotification cn,
JMXConnector emittingConnector) {
// Due to the asynchronous handling of notifications, it is
// possible that this method is called for a JMXConnector
// (or connection) which is already closed and replaced by a newer
// one.
//
// This method attempts to determine the real state of the
// connection - which might be different from what the notification
// says.
//
// This is quite complex logic - because we try not to hold any
// lock while evaluating the true value of the connected state,
// while anyone might also call close() or connect() from a
// different thread.
//
// The method switchConnection() (called from here too) also has the
// same kind of complex logic.
//
// We use the JMXConnector has a handback to the notification listener
// (emittingConnector) in order to be able to determine whether the
// notification concerns the current connector in use, or an older
// one.
//
boolean remove = false;
// whether the emittingConnector is already 'removed'
synchronized (this) {
if (this.conn != emittingConnector ||
JMXConnectionNotification.FAILED.equals(cn.getType()))
remove = true;
}
// We need to unregister our listener from this 'removed' connector.
// This is the only place where we remove the listener.
//
if (remove) {
try {
// This may fail if the connector is already closed.
// But better unregister anyway...
//
emittingConnector.removeConnectionNotificationListener(
listener,null,
emittingConnector);
} catch (Exception x) {
LOG.log(Level.FINE,
"Failed to unregister connection listener"+x);
LOG.log(Level.FINEST,
"Failed to unregister connection listener",x);
}
try {
// This may fail if the connector is already closed.
// But better call close twice and get an exception than
// leaking...
//
emittingConnector.close();
} catch (Exception x) {
LOG.log(Level.FINEST,
"Failed to close old connector " +
"(failure was expected): "+x);
}
}
// Now we checked whether our current connector is still alive.
//
boolean closed = false;
final JMXConnector thisconn = this.conn;
try {
if (thisconn != null)
thisconn.getConnectionId();
} catch (IOException x) {
LOG.finest("Connector already closed: "+x);
closed = true;
}
// We got an IOException - the connector is not connected.
// Need to forget it and switch our state to closed.
//
if (closed) {
switchConnection(thisconn,null,null);
try {
// Usually this will fail... Better call close twice
// and get an exception than leaking...
//
if (thisconn != emittingConnector || !remove)
thisconn.close();
} catch (IOException x) {
LOG.log(Level.FINEST,
"Failed to close connector (failure was expected): "
+x);
}
}
}
private final void switchConnection(JMXConnector oldc,
JMXConnector newc,
MBeanServerConnection mbs) {
boolean connect = false;
boolean close = false;
synchronized (this) {
if (oldc != conn) {
if (newc != null) {
try {
newc.close();
} catch (IOException x) {
LOG.log(Level.FINEST,
"Failed to close connector",x);
}
}
return;
}
if (conn == null && newc != null) connect=true;
if (newc == null && conn != null) close = true;
conn = newc;
server = mbs;
}
if (connect || close) {
boolean oldstate = close;
boolean newstate = connect;
final ObjectName myName = getObjectName();
// In the uncommon case where the MBean is connected before
// being registered, myName can be null...
// If myName is null - we use 'this' as the source instead...
//
final Object source = (myName==null)?this:myName;
final AttributeChangeNotification acn =
new AttributeChangeNotification(source,
getNextSeqNumber(),System.currentTimeMillis(),
String.valueOf(source)+
(newstate?" connected":" closed"),
"Connected",
"boolean",
Boolean.valueOf(oldstate),
Boolean.valueOf(newstate));
sendNotification(acn);
}
}
private void closeall(JMXConnector... a) {
for (JMXConnector c : a) {
try {
if (c != null) c.close();
} catch (Exception x) {
// OK: we're gonna throw the original exception later.
LOG.finest("Ignoring exception when closing connector: "+x);
}
}
}
JMXConnector connect(JMXServiceURL url, Map<String,?> env)
throws IOException {
final JMXConnector c = newJMXConnector(jmxURL, env);
c.connect(env);
return c;
}
/**
* Creates a new JMXConnector with the specified {@code url} and
* {@code env} options map.
* <p>
* This method first calls {@link JMXConnectorFactory#newJMXConnector
* JMXConnectorFactory.newJMXConnector(jmxURL, env)} to obtain a new
* JMX connector, and returns that.
* </p>
* <p>
* A subclass of {@link JMXRemoteNamespace} can provide an implementation
* that connects to a sub namespace of the remote server by subclassing
* this class in the following way:
* <pre>
* class JMXRemoteSubNamespace extends JMXRemoteNamespace {
* private final String subnamespace;
* JMXRemoteSubNamespace(JMXServiceURL url,
* Map{@code <String,?>} env, String subnamespace) {
* super(url,options);
* this.subnamespace = subnamespace;
* }
* protected JMXConnector newJMXConnector(JMXServiceURL url,
* Map<String,?> env) throws IOException {
* final JMXConnector inner = super.newJMXConnector(url,env);
* return {@link JMXNamespaces#narrowToNamespace(JMXConnector,String)
* JMXNamespaces.narrowToNamespace(inner,subnamespace)};
* }
* }
* </pre>
* </p>
* <p>
* Some connectors, like the JMXMP connector server defined by the
* version 1.2 of the JMX API may not have been upgraded to use the
* new {@linkplain javax.management.event Event Service} defined in this
* version of the JMX API.
* <p>
* In that case, and if the remote server to which this JMXRemoteNamespace
* connects also contains namespaces, it may be necessary to configure
* explicitly an {@linkplain
* javax.management.event.EventClientDelegate#newForwarder()
* Event Client Forwarder} on the remote server side, and to force the use
* of an {@link EventClient} on this client side.
* <br>
* A subclass of {@link JMXRemoteNamespace} can provide an implementation
* of {@code newJMXConnector} that will force notification subscriptions
* to flow through an {@link EventClient} over a legacy protocol by
* overriding this method in the following way:
* </p>
* <pre>
* class JMXRemoteEventClientNamespace extends JMXRemoteNamespace {
* JMXRemoteSubNamespaceConnector(JMXServiceURL url,
* Map<String,?> env) {
* super(url,options);
* }
* protected JMXConnector newJMXConnector(JMXServiceURL url,
* Map<String,?> env) throws IOException {
* final JMXConnector inner = super.newJMXConnector(url,env);
* return {@link EventClient#withEventClient(
* JMXConnector) EventClient.withEventClient(inner)};
* }
* }
* </pre>
* <p>
* Note that the remote server also needs to provide an {@link
* javax.management.event.EventClientDelegateMBean}: only configuring
* the client side (this object) is not enough.<br>
* In summary, this technique should be used if the remote server
* supports JMX namespaces, but uses a JMX Connector Server whose
* implementation does not transparently use the new Event Service
* (as would be the case with the JMXMPConnectorServer implementation
* from the reference implementation of the JMX Remote API 1.0
* specification).
* </p>
* @param url The JMXServiceURL of the remote server.
* @param optionsMap An unmodifiable options map that will be passed to the
* {@link JMXConnectorFactory} when {@linkplain
* JMXConnectorFactory#newJMXConnector creating} the
* {@link JMXConnector} that can connect to the remote source
* MBean Server.
* @return An unconnected JMXConnector to use to connect to the remote
* server
* @throws java.io.IOException if the connector could not be created.
* @see JMXConnectorFactory#newJMXConnector(javax.management.remote.JMXServiceURL, java.util.Map)
* @see #JMXRemoteNamespace
*/
protected JMXConnector newJMXConnector(JMXServiceURL url,
Map<String,?> optionsMap) throws IOException {
final JMXConnector c =
JMXConnectorFactory.newJMXConnector(jmxURL, optionsMap);
// TODO: uncomment this when contexts are added
// return ClientContext.withDynamicContext(c);
return c;
}
public void connect() throws IOException {
if (conn != null) {
try {
// This is much too fragile. It must go away!
PROBE_LOG.finest("Probing again...");
triggerProbe(getMBeanServerConnection());
} catch(Exception x) {
close();
Throwable cause = x;
// if the cause is a security exception - rethrows it...
while (cause != null) {
if (cause instanceof SecurityException)
throw (SecurityException) cause;
cause = cause.getCause();
}
throw new IOException("connection failed: cycle?",x);
}
}
LOG.fine("connecting...");
// TODO remove these traces
// System.err.println(getInitParameter()+" connecting");
final Map<String,Object> env =
new HashMap<String,Object>(getEnvMap());
try {
// XXX: We should probably document this...
// This allows to specify a loader name - which will be
// retrieved from the paret MBeanServer.
defaultClassLoader =
EnvHelp.resolveServerClassLoader(env,getMBeanServer());
} catch (InstanceNotFoundException x) {
final IOException io =
new IOException("ClassLoader not found");
io.initCause(x);
throw io;
}
env.put(JMXConnectorFactory.DEFAULT_CLASS_LOADER,defaultClassLoader);
final JMXServiceURL url = getJMXServiceURL();
final JMXConnector aconn = connect(url,env);
final MBeanServerConnection msc;
try {
msc = aconn.getMBeanServerConnection();
aconn.addConnectionNotificationListener(listener,null,aconn);
} catch (IOException io) {
closeall(aconn);
throw io;
} catch (RuntimeException x) {
closeall(aconn);
throw x;
}
// XXX Revisit here
// Note from the author: This business of switching connection is
// incredibly complex. Isn't there any means to simplify it?
//
switchConnection(conn,aconn,msc);
try {
triggerProbe(msc);
} catch(Exception x) {
close();
Throwable cause = x;
// if the cause is a security exception - rethrows it...
while (cause != null) {
if (cause instanceof SecurityException)
throw (SecurityException) cause;
cause = cause.getCause();
}
throw new IOException("connection failed: cycle?",x);
}
LOG.fine("connected.");
}
// If this is a self-linking namespace, this method should trigger
// the emission of a probe in the wrapping NamespaceInterceptor.
// The first call to source() in the wrapping NamespaceInterceptor
// causes the emission of the probe.
//
// Note: the MBeanServer returned by getSourceServer
// (our private JMXRemoteNamespaceDelegate inner class)
// implements a sun private interface (DynamicProbe) which is
// used by the NamespaceInterceptor to determine whether it should
// send a probe or not.
// We needed this interface here because the NamespaceInterceptor
// has otherwise no means to knows that this object has just
// connected, and that a new probe should be sent.
//
// Probes work this way: the NamespaceInterceptor sets a flag and sends
// a queryNames() request. If a queryNames() request comes in when the flag
// is on, then it deduces that there is a self-linking loop - and instead
// of calling queryNames() on the JMXNamespace (which would cause the
// loop to go on) it breaks the recursion by returning the probe ObjectName.
// If the NamespaceInterceptor receives the probe ObjectName as result of
// its original queryNames() it knows that it has been looping back on
// itslef and throws an Exception - which will be raised through this
// method, thus preventing the connection to be established...
//
// More info in the com.sun.jmx.namespace.NamespaceInterceptor class
//
// XXX: TODO this probe thing is way too complex and fragile.
// This *must* go away or be replaced by something simpler.
// ideas are welcomed.
//
private void triggerProbe(final MBeanServerConnection msc)
throws MalformedObjectNameException, IOException {
// Query Pattern that we will send through the source server in order
// to detect self-linking namespaces.
//
//
final ObjectName pattern;
pattern = ObjectName.getInstance("*" +
JMXNamespaces.NAMESPACE_SEPARATOR + ":" +
JMXNamespace.TYPE_ASSIGNMENT);
probed = false;
try {
msc.queryNames(pattern, null);
probed = true;
} catch (AccessControlException x) {
// if we have an MBeanPermission missing then do nothing...
if (!(x.getPermission() instanceof MBeanPermission))
throw x;
PROBE_LOG.finer("Can't check for cycles: " + x);
probed = false; // no need to do it again...
}
}
public void close() throws IOException {
if (conn == null) return;
LOG.fine("closing...");
// System.err.println(toString()+": closing...");
conn.close();
// System.err.println(toString()+": connector closed");
switchConnection(conn,null,null);
LOG.fine("closed.");
// System.err.println(toString()+": closed");
}
MBeanServerConnection getMBeanServerConnection() {
if (conn == null)
throw newRuntimeIOException("getMBeanServerConnection: not connected");
return server;
}
// Better than throwing UndeclaredThrowableException ...
private RuntimeException newRuntimeIOException(String msg) {
final IllegalStateException illegal = new IllegalStateException(msg);
return Util.newRuntimeIOException(new IOException(msg,illegal));
}
/**
* Returns the default class loader used by the underlying
* {@link JMXConnector}.
* @return the default class loader used when communicating with the
* remote source MBean server.
**/
ClassLoader getDefaultClassLoader() {
if (conn == null)
throw newRuntimeIOException("getMBeanServerConnection: not connected");
return defaultClassLoader;
}
public boolean isConnected() {
// This is a pleonasm
return (conn != null) && (server != null);
}
/**
* This name space handler will automatically {@link #close} its
* connection with the remote source in {@code preDeregister}.
**/
@Override
public void preDeregister() throws Exception {
try {
close();
} catch (IOException x) {
LOG.fine("Failed to close properly - exception ignored: " + x);
LOG.log(Level.FINEST,
"Failed to close properly - exception ignored",x);
}
super.preDeregister();
}
/**
* This method calls {@link
* javax.management.MBeanServerConnection#getMBeanCount
* getMBeanCount()} on the remote namespace.
* @throws java.io.IOException if an {@link IOException} is raised when
* communicating with the remote source namespace.
*/
@Override
public Integer getMBeanCount() throws IOException {
return getMBeanServerConnection().getMBeanCount();
}
/**
* This method returns the result of calling {@link
* javax.management.MBeanServerConnection#getDomains
* getDomains()} on the remote namespace.
* @throws java.io.IOException if an {@link IOException} is raised when
* communicating with the remote source namespace.
*/
@Override
public String[] getDomains() throws IOException {
return getMBeanServerConnection().getDomains();
}
/**
* This method returns the result of calling {@link
* javax.management.MBeanServerConnection#getDefaultDomain
* getDefaultDomain()} on the remote namespace.
* @throws java.io.IOException if an {@link IOException} is raised when
* communicating with the remote source namespace.
*/
@Override
public String getDefaultDomain() throws IOException {
return getMBeanServerConnection().getDefaultDomain();
}
/**
* Creates a new instance of {@code JMXRemoteNamespace}.
* @param sourceURL a JMX service URL that can be used to connect to the
* source MBean Server. The source MBean Server is the remote
* MBean Server which contains the MBeans that will be mirrored
* in this namespace.
* @param optionsMap An options map that will be passed to the
* {@link JMXConnectorFactory} when {@linkplain
* JMXConnectorFactory#newJMXConnector creating} the
* {@link JMXConnector} used to connect to the remote source
* MBean Server. Can be null, which is equivalent to an empty map.
* @see #JMXRemoteNamespace JMXRemoteNamespace(sourceURL,optionsMap)
* @see JMXConnectorFactory#newJMXConnector(javax.management.remote.JMXServiceURL, java.util.Map)
*/
public static JMXRemoteNamespace newJMXRemoteNamespace(
JMXServiceURL sourceURL,
Map<String,?> optionsMap) {
return new JMXRemoteNamespace(sourceURL, optionsMap);
}
}

View file

@ -0,0 +1,96 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package javax.management.namespace;
import java.io.IOException;
import javax.management.remote.JMXServiceURL;
/**
* A {@link JMXNamespaceMBean} that will connect to a remote MBeanServer
* by creating a {@link javax.management.remote.JMXConnector} from a
* {@link javax.management.remote.JMXServiceURL}.
* You can call {@link #connect connect()} and {@link #close close()}
* several times.
* @since 1.7
*/
public interface JMXRemoteNamespaceMBean
extends JMXNamespaceMBean {
/**
* Connects to the underlying remote source name space, if not already
* {@link #isConnected connected}.
* If connected, do nothing. Otherwise, creates a new connector from the
* {@link javax.management.remote.JMXServiceURL JMXServiceURL} provided at
* creation time, and connects to the remote source name space.
* <p>
* The source MBeans will not appear in the target name space until the
* JMXRemoteNamespaceMBean is connected.
* </p><p>
* It is possible to call {@code connect()}, {@link #close close()}, and
* {@code connect()} again.
* However, closing the connection with the remote name space may cause
* notification listeners to be lost, unless the client explicitly uses
* the new {@linkplain javax.management.event JMX event service}.
* </p><p>
* @throws IOException if connection to the remote source name space fails.
* @see #isConnected isConnected
**/
public void connect()
throws IOException;
/**
* Closes the connection with the remote source name space.
* If the connection is already closed, do nothing.
* Otherwise, closes the underlying {@link
* javax.management.remote.JMXConnector}.
* <p>Once closed, it is possible to reopen the connection by
* calling {@link #connect connect}.
* </p>
* @throws IOException if the connection to the remote source name space
* can't be closed properly.
* @see #isConnected isConnected
**/
public void close()
throws IOException;
/**
* Tells whether the connection to the remote source name space is opened.
* @see #connect connect
* @see #close close
* @return {@code true} if connected.
**/
public boolean isConnected();
/**
* Returns the {@link JMXServiceURL} address that points to the remote name
* space mirrored by this {@link JMXNamespaceMBean JMXNamespace MBean},
* if available.
* @return The {@link JMXServiceURL} address that points to the remote name
* space mirrored by this {@link JMXNamespaceMBean JMXNamespace MBean},
* or {@code null}.
*/
public JMXServiceURL getAddress();
}

View file

@ -0,0 +1,703 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package javax.management.namespace;
import com.sun.jmx.mbeanserver.Util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.security.AccessController;
import java.util.Set;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.OperationsException;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.management.loading.ClassLoaderRepository;
/**
* <p>An object of this class implements the MBeanServer interface
* and, for each of its methods forwards the request to a wrapped
* {@link MBeanServerConnection} object.
* Some methods of the {@link MBeanServer} interface do not have
* any equivalent in {@link MBeanServerConnection}. In that case, an
* {@link UnsupportedOperationException} will be thrown.
*
* <p>A typical use of this class is to apply a {@link QueryExp} object locally,
* on an MBean that resides in a remote MBeanServer. Since an
* MBeanServerConnection is not an MBeanServer, it cannot be passed
* to the <code>setMBeanServer()</code> method of the {@link QueryExp}
* object. However, this object can.</p>
*
* @since 1.7
*/
public class MBeanServerConnectionWrapper
implements MBeanServer {
private final MBeanServerConnection wrapped;
private final ClassLoader defaultCl;
/**
* Construct a new object that implements {@link MBeanServer} by
* forwarding its methods to the given {@link MBeanServerConnection}.
* This constructor is equivalent to {@link #MBeanServerConnectionWrapper(
* MBeanServerConnection, ClassLoader) MBeanServerConnectionWrapper(wrapped,
* null)}.
*
* @param wrapped the {@link MBeanServerConnection} to which methods
* are to be forwarded. This parameter can be null, in which case the
* {@code MBeanServerConnection} will typically be supplied by overriding
* {@link #getMBeanServerConnection}.
*/
public MBeanServerConnectionWrapper(MBeanServerConnection wrapped) {
this(wrapped, null);
}
/**
* Construct a new object that implements {@link MBeanServer} by
* forwarding its methods to the given {@link MBeanServerConnection}.
* The {@code defaultCl} parameter specifies the value to be returned
* by {@link #getDefaultClassLoader}. A null value is equivalent to
* {@link Thread#getContextClassLoader()}.
*
* @param wrapped the {@link MBeanServerConnection} to which methods
* are to be forwarded. This parameter can be null, in which case the
* {@code MBeanServerConnection} will typically be supplied by overriding
* {@link #getMBeanServerConnection}.
* @param defaultCl the value to be returned by {@link
* #getDefaultClassLoader}. A null value is equivalent to the current
* thread's {@linkplain Thread#getContextClassLoader()}.
*/
public MBeanServerConnectionWrapper(MBeanServerConnection wrapped,
ClassLoader defaultCl) {
this.wrapped = wrapped;
this.defaultCl = (defaultCl == null) ?
Thread.currentThread().getContextClassLoader() : defaultCl;
}
/**
* Returns an MBeanServerConnection. This method is called each time
* an operation must be invoked on the underlying MBeanServerConnection.
* The default implementation returns the MBeanServerConnection that
* was supplied to the constructor of this MBeanServerConnectionWrapper.
**/
protected MBeanServerConnection getMBeanServerConnection() {
return wrapped;
}
/**
* Returns the default class loader passed to the constructor. If the
* value passed was null, then the returned value will be the
* {@linkplain Thread#getContextClassLoader() context class loader} at the
* time this object was constructed.
*
* @return the ClassLoader that was passed to the constructor.
**/
public ClassLoader getDefaultClassLoader() {
return defaultCl;
}
/**
* <p>This method is called each time an IOException is raised when
* trying to forward an operation to the underlying
* MBeanServerConnection, as a result of calling
* {@link #getMBeanServerConnection()} or as a result of invoking the
* operation on the returned connection. Since the methods in
* {@link MBeanServer} are not declared to throw {@code IOException},
* this method must return a {@code RuntimeException} to be thrown
* instead. Typically, the original {@code IOException} will be in the
* {@linkplain Throwable#getCause() cause chain} of the {@code
* RuntimeException}.</p>
*
* <p>Subclasses may redefine this method if they need to perform any
* specific handling of IOException (logging etc...).</p>
*
* @param x The raised IOException.
* @param method The name of the method in which the exception was
* raised. This is one of the methods of the MBeanServer
* interface.
*
* @return A RuntimeException that should be thrown by the caller.
* In this default implementation, this is a
* {@link RuntimeException} wrapping <var>x</var>.
**/
protected RuntimeException wrapIOException(IOException x, String method) {
return Util.newRuntimeIOException(x);
}
// Take care of getMBeanServerConnection returning null.
//
private synchronized MBeanServerConnection connection()
throws IOException {
final MBeanServerConnection c = getMBeanServerConnection();
if (c == null)
throw new IOException("MBeanServerConnection unavailable");
return c;
}
//--------------------------------------------
//--------------------------------------------
//
// Implementation of the MBeanServer interface
//
//--------------------------------------------
//--------------------------------------------
/**
* Forward this method to the
* wrapped object.
*/
public void addNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException {
try {
connection().addNotificationListener(name, listener,
filter, handback);
} catch (IOException x) {
throw wrapIOException(x,"addNotificationListener");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public void addNotificationListener(ObjectName name,
ObjectName listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException {
try {
connection().addNotificationListener(name, listener,
filter, handback);
} catch (IOException x) {
throw wrapIOException(x,"addNotificationListener");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public ObjectInstance createMBean(String className, ObjectName name)
throws
ReflectionException,
InstanceAlreadyExistsException,
MBeanRegistrationException,
MBeanException,
NotCompliantMBeanException {
try {
return connection().createMBean(className, name);
} catch (IOException x) {
throw wrapIOException(x,"createMBean");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public ObjectInstance createMBean(String className, ObjectName name,
Object params[], String signature[])
throws
ReflectionException,
InstanceAlreadyExistsException,
MBeanRegistrationException,
MBeanException,
NotCompliantMBeanException {
try {
return connection().createMBean(className, name,
params, signature);
} catch (IOException x) {
throw wrapIOException(x,"createMBean");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public ObjectInstance createMBean(String className,
ObjectName name,
ObjectName loaderName)
throws
ReflectionException,
InstanceAlreadyExistsException,
MBeanRegistrationException,
MBeanException,
NotCompliantMBeanException,
InstanceNotFoundException {
try {
return connection().createMBean(className, name, loaderName);
} catch (IOException x) {
throw wrapIOException(x,"createMBean");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public ObjectInstance createMBean(String className,
ObjectName name,
ObjectName loaderName,
Object params[],
String signature[])
throws
ReflectionException,
InstanceAlreadyExistsException,
MBeanRegistrationException,
MBeanException,
NotCompliantMBeanException,
InstanceNotFoundException {
try {
return connection().createMBean(className, name, loaderName,
params, signature);
} catch (IOException x) {
throw wrapIOException(x,"createMBean");
}
}
/**
* Throws an {@link UnsupportedOperationException}. This behavior can
* be changed by subclasses.
* @deprecated see {@link MBeanServer#deserialize(ObjectName,byte[])
* MBeanServer}
*/
@Deprecated
public ObjectInputStream deserialize(ObjectName name, byte[] data)
throws InstanceNotFoundException, OperationsException {
throw new UnsupportedOperationException("deserialize");
}
/**
* Throws an {@link UnsupportedOperationException}. This behavior can
* be changed by subclasses.
* @deprecated see {@link MBeanServer#deserialize(String,byte[])
* MBeanServer}
*/
@Deprecated
public ObjectInputStream deserialize(String className, byte[] data)
throws OperationsException, ReflectionException {
throw new UnsupportedOperationException("deserialize");
}
/**
* Throws an {@link UnsupportedOperationException}. This behavior can
* be changed by subclasses.
* @deprecated see {@link MBeanServer#deserialize(String,ObjectName,byte[])
* MBeanServer}
*/
@Deprecated
public ObjectInputStream deserialize(String className,
ObjectName loaderName,
byte[] data)
throws
InstanceNotFoundException,
OperationsException,
ReflectionException {
throw new UnsupportedOperationException("deserialize");
}
/**
* Forward this method to the
* wrapped object.
*/
public Object getAttribute(ObjectName name, String attribute)
throws
MBeanException,
AttributeNotFoundException,
InstanceNotFoundException,
ReflectionException {
try {
return connection().getAttribute(name, attribute);
} catch (IOException x) {
throw wrapIOException(x,"getAttribute");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public AttributeList getAttributes(ObjectName name, String[] attributes)
throws InstanceNotFoundException, ReflectionException {
try {
return connection().getAttributes(name, attributes);
} catch (IOException x) {
throw wrapIOException(x,"getAttributes");
}
}
/**
* Throws an {@link UnsupportedOperationException}. This behavior can
* be changed by subclasses.
*/
public ClassLoader getClassLoader(ObjectName loaderName)
throws InstanceNotFoundException {
throw new UnsupportedOperationException("getClassLoader");
}
/**
* Returns the {@linkplain #getDefaultClassLoader() default class loader}.
* This behavior can be changed by subclasses.
*/
public ClassLoader getClassLoaderFor(ObjectName mbeanName)
throws InstanceNotFoundException {
return getDefaultClassLoader();
}
/**
* <p>Returns a {@link ClassLoaderRepository} based on the class loader
* returned by {@link #getDefaultClassLoader()}.</p>
* @return a {@link ClassLoaderRepository} that contains a single
* class loader, returned by {@link #getDefaultClassLoader()}.
**/
public ClassLoaderRepository getClassLoaderRepository() {
// We return a new ClassLoaderRepository each time this method is
// called. This is by design, because there's no guarantee that
// getDefaultClassLoader() will always return the same class loader.
return Util.getSingleClassLoaderRepository(getDefaultClassLoader());
}
/**
* Forward this method to the
* wrapped object.
*/
public String getDefaultDomain() {
try {
return connection().getDefaultDomain();
} catch (IOException x) {
throw wrapIOException(x,"getDefaultDomain");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public String[] getDomains() {
try {
return connection().getDomains();
} catch (IOException x) {
throw wrapIOException(x,"getDomains");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public Integer getMBeanCount() {
try {
return connection().getMBeanCount();
} catch (IOException x) {
throw wrapIOException(x,"getMBeanCount");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public MBeanInfo getMBeanInfo(ObjectName name)
throws
InstanceNotFoundException,
IntrospectionException,
ReflectionException {
try {
return connection().getMBeanInfo(name);
} catch (IOException x) {
throw wrapIOException(x,"getMBeanInfo");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public ObjectInstance getObjectInstance(ObjectName name)
throws InstanceNotFoundException {
try {
return connection().getObjectInstance(name);
} catch (IOException x) {
throw wrapIOException(x,"getObjectInstance");
}
}
/**
* Throws an {@link UnsupportedOperationException}. This behavior can
* be changed by subclasses.
*/
public Object instantiate(String className)
throws ReflectionException, MBeanException {
throw new UnsupportedOperationException("instantiate");
}
/**
* Throws an {@link UnsupportedOperationException}. This behavior can
* be changed by subclasses.
*/
public Object instantiate(String className,
Object params[],
String signature[])
throws ReflectionException, MBeanException {
throw new UnsupportedOperationException("instantiate");
}
/**
* Throws an {@link UnsupportedOperationException}. This behavior can
* be changed by subclasses.
*/
public Object instantiate(String className, ObjectName loaderName)
throws ReflectionException, MBeanException,
InstanceNotFoundException {
throw new UnsupportedOperationException("instantiate");
}
/**
* Throws an {@link UnsupportedOperationException}. This behavior can
* be changed by subclasses.
*/
public Object instantiate(String className, ObjectName loaderName,
Object params[], String signature[])
throws ReflectionException, MBeanException,
InstanceNotFoundException {
throw new UnsupportedOperationException("instantiate");
}
/**
* Forward this method to the
* wrapped object.
*/
public Object invoke(ObjectName name, String operationName,
Object params[], String signature[])
throws
InstanceNotFoundException,
MBeanException,
ReflectionException {
try {
return connection().invoke(name,operationName,params,signature);
} catch (IOException x) {
throw wrapIOException(x,"invoke");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public boolean isInstanceOf(ObjectName name, String className)
throws InstanceNotFoundException {
try {
return connection().isInstanceOf(name, className);
} catch (IOException x) {
throw wrapIOException(x,"isInstanceOf");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public boolean isRegistered(ObjectName name) {
try {
return connection().isRegistered(name);
} catch (IOException x) {
throw wrapIOException(x,"isRegistered");
}
}
/**
* Forward this method to the
* wrapped object.
* If an IOException is raised, returns an empty Set.
*/
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
try {
return connection().queryMBeans(name, query);
} catch (IOException x) {
throw wrapIOException(x,"queryMBeans");
//return Collections.emptySet();
}
}
/**
* Forward this method to the
* wrapped object.
* If an IOException is raised, returns an empty Set.
*/
public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
try {
return connection().queryNames(name, query);
} catch (IOException x) {
throw wrapIOException(x,"queryNames");
//return Collections.emptySet();
}
}
/**
* Throws an {@link UnsupportedOperationException}. This behavior can
* be changed by subclasses.
*/
public ObjectInstance registerMBean(Object object, ObjectName name)
throws
InstanceAlreadyExistsException,
MBeanRegistrationException,
NotCompliantMBeanException {
throw new UnsupportedOperationException("registerMBean");
}
/**
* Forward this method to the
* wrapped object.
*/
public void removeNotificationListener(ObjectName name,
NotificationListener listener)
throws InstanceNotFoundException, ListenerNotFoundException {
try {
connection().removeNotificationListener(name, listener);
} catch (IOException x) {
throw wrapIOException(x,"removeNotificationListener");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public void removeNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException, ListenerNotFoundException {
try {
connection().removeNotificationListener(name, listener,
filter, handback);
} catch (IOException x) {
throw wrapIOException(x,"removeNotificationListener");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public void removeNotificationListener(ObjectName name,
ObjectName listener)
throws InstanceNotFoundException, ListenerNotFoundException {
try {
connection().removeNotificationListener(name, listener);
} catch (IOException x) {
throw wrapIOException(x,"removeNotificationListener");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public void removeNotificationListener(ObjectName name,
ObjectName listener,
NotificationFilter filter,
Object handback)
throws InstanceNotFoundException, ListenerNotFoundException {
try {
connection().removeNotificationListener(name, listener,
filter, handback);
} catch (IOException x) {
throw wrapIOException(x,"removeNotificationListener");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public void setAttribute(ObjectName name, Attribute attribute)
throws
InstanceNotFoundException,
AttributeNotFoundException,
InvalidAttributeValueException,
MBeanException,
ReflectionException {
try {
connection().setAttribute(name, attribute);
} catch (IOException x) {
throw wrapIOException(x,"setAttribute");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public AttributeList setAttributes(ObjectName name,
AttributeList attributes)
throws InstanceNotFoundException, ReflectionException {
try {
return connection().setAttributes(name, attributes);
} catch (IOException x) {
throw wrapIOException(x,"setAttributes");
}
}
/**
* Forward this method to the
* wrapped object.
*/
public void unregisterMBean(ObjectName name)
throws InstanceNotFoundException, MBeanRegistrationException {
try {
connection().unregisterMBean(name);
} catch (IOException x) {
throw wrapIOException(x,"unregisterMBean");
}
}
//----------------
// PRIVATE METHODS
//----------------
}

View file

@ -1,5 +1,5 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -22,8 +22,9 @@
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.interceptor;
package javax.management.namespace;
import com.sun.jmx.defaults.JmxProperties;
import com.sun.jmx.mbeanserver.Util;
import java.io.ObjectInputStream;
import java.util.Collections;
@ -47,7 +48,6 @@ import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationEmitter;
@ -343,7 +343,7 @@ import javax.management.loading.ClassLoaderRepository;
* vem.publish}<code>(<em>name</em>, <em>n</em>)</code>. See the example
* <a href="#virtual-notif-example">above</a>.</p>
*
* @since Java SE 7
* @since 1.7
*/
public abstract class MBeanServerSupport implements MBeanServer {
@ -351,7 +351,7 @@ public abstract class MBeanServerSupport implements MBeanServer {
* A logger for this class.
*/
private static final Logger LOG =
Logger.getLogger(MBeanServerSupport.class.getName());
JmxProperties.NAMESPACE_LOGGER;
/**
* <p>Make a new {@code MBeanServerSupport} instance.</p>
@ -1314,9 +1314,7 @@ public abstract class MBeanServerSupport implements MBeanServer {
if (name.getDomain().equals("")) {
String defaultDomain = getDefaultDomain();
try {
// XXX change to ObjectName.switchDomain
// current code DOES NOT PRESERVE the order of keys
name = new ObjectName(defaultDomain, name.getKeyPropertyList());
name = name.withDomain(getDefaultDomain());
} catch (Exception e) {
throw newIllegalArgumentException(
"Illegal default domain: " + defaultDomain);

View file

@ -0,0 +1,378 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package javax.management.namespace;
import com.sun.jmx.remote.util.ClassLogger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.management.InstanceNotFoundException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.Notification;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.event.EventConsumer;
/**
* <p>This class maintains a list of subscribers for ObjectName patterns and
* allows a notification to be sent to all subscribers for a given ObjectName.
* It is typically used in conjunction with {@link MBeanServerSupport}
* to implement a namespace with Virtual MBeans that can emit notifications.
* The {@code VirtualEventManager} keeps track of the listeners that have been
* added to each Virtual MBean. When an event occurs that should trigger a
* notification from a Virtual MBean, the {@link #publish publish} method can
* be used to send it to the appropriate listeners.</p>
* @since 1.7
*/
public class VirtualEventManager implements EventConsumer {
/**
* <p>Create a new {@code VirtualEventManager}.</p>
*/
public VirtualEventManager() {
}
public void subscribe(
ObjectName name,
NotificationListener listener,
NotificationFilter filter,
Object handback) {
if (logger.traceOn())
logger.trace("subscribe", "" + name);
if (name == null)
throw new IllegalArgumentException("Null MBean name");
if (listener == null)
throw new IllegalArgumentException("Null listener");
Map<ObjectName, List<ListenerInfo>> map =
name.isPattern() ? patternSubscriptionMap : exactSubscriptionMap;
final ListenerInfo li = new ListenerInfo(listener, filter, handback);
List<ListenerInfo> list;
synchronized (map) {
list = map.get(name);
if (list == null) {
list = new ArrayList<ListenerInfo>();
map.put(name, list);
}
list.add(li);
}
}
public void unsubscribe(
ObjectName name, NotificationListener listener)
throws ListenerNotFoundException {
if (logger.traceOn())
logger.trace("unsubscribe2", "" + name);
if (name == null)
throw new IllegalArgumentException("Null MBean name");
if (listener == null)
throw new ListenerNotFoundException();
Map<ObjectName, List<ListenerInfo>> map =
name.isPattern() ? patternSubscriptionMap : exactSubscriptionMap;
final ListenerInfo li = new ListenerInfo(listener, null, null);
List<ListenerInfo> list;
synchronized (map) {
list = map.get(name);
if (list == null || !list.remove(li))
throw new ListenerNotFoundException();
if (list.isEmpty())
map.remove(name);
}
}
/**
* <p>Unsubscribes a listener which is listening to an MBean or a set of
* MBeans represented by an {@code ObjectName} pattern.</p>
*
* <p>The listener to be removed must have been added by the {@link
* #subscribe subscribe} method with the given {@code name}, {@code filter},
* and {@code handback}. If the {@code
* name} is a pattern, then the {@code subscribe} must have used the same
* pattern. If the same listener has been subscribed more than once to the
* {@code name} with the same filter and handback, only one listener is
* removed.</p>
*
* @param name The name of the MBean or an {@code ObjectName} pattern
* representing a set of MBeans to which the listener was subscribed.
* @param listener A listener that was previously subscribed to the
* MBean(s).
*
* @throws ListenerNotFoundException The given {@code listener} was not
* subscribed to the given {@code name}.
*
* @see #subscribe
*/
public void unsubscribe(
ObjectName name, NotificationListener listener,
NotificationFilter filter, Object handback)
throws ListenerNotFoundException {
if (logger.traceOn())
logger.trace("unsubscribe4", "" + name);
if (name == null)
throw new IllegalArgumentException("Null MBean name");
if (listener == null)
throw new ListenerNotFoundException();
Map<ObjectName, List<ListenerInfo>> map =
name.isPattern() ? patternSubscriptionMap : exactSubscriptionMap;
List<ListenerInfo> list;
synchronized (map) {
list = map.get(name);
boolean removed = false;
for (Iterator<ListenerInfo> it = list.iterator(); it.hasNext(); ) {
ListenerInfo li = it.next();
if (li.equals(listener, filter, handback)) {
it.remove();
removed = true;
break;
}
}
if (!removed)
throw new ListenerNotFoundException();
if (list.isEmpty())
map.remove(name);
}
}
/**
* <p>Sends a notification to the subscribers for a given MBean.</p>
*
* <p>For each listener subscribed with an {@code ObjectName} that either
* is equal to {@code emitterName} or is a pattern that matches {@code
* emitterName}, if the associated filter accepts the notification then it
* is forwarded to the listener.</p>
*
* @param emitterName The name of the MBean emitting the notification.
* @param n The notification being sent by the MBean called
* {@code emitterName}.
*
* @throws IllegalArgumentException If the emitterName of the
* notification is null or is an {@code ObjectName} pattern.
*/
public void publish(ObjectName emitterName, Notification n) {
if (logger.traceOn())
logger.trace("publish", "" + emitterName);
if (n == null)
throw new IllegalArgumentException("Null notification");
if (emitterName == null) {
throw new IllegalArgumentException(
"Null emitter name");
} else if (emitterName.isPattern()) {
throw new IllegalArgumentException(
"The emitter must not be an ObjectName pattern");
}
final List<ListenerInfo> listeners = new ArrayList<ListenerInfo>();
// If there are listeners for this exact name, add them.
synchronized (exactSubscriptionMap) {
List<ListenerInfo> exactListeners =
exactSubscriptionMap.get(emitterName);
if (exactListeners != null)
listeners.addAll(exactListeners);
}
// Loop over subscription patterns, and add all listeners for each
// one that matches the emitterName name.
synchronized (patternSubscriptionMap) {
for (ObjectName on : patternSubscriptionMap.keySet()) {
if (on.apply(emitterName))
listeners.addAll(patternSubscriptionMap.get(on));
}
}
// Send the notification to all the listeners we found.
sendNotif(listeners, n);
}
/**
* <p>Returns a {@link NotificationEmitter} object which can be used to
* subscribe or unsubscribe for notifications with the named
* mbean. The returned object implements {@link
* NotificationEmitter#addNotificationListener
* addNotificationListener(listener, filter, handback)} as
* {@link #subscribe this.subscribe(name, listener, filter, handback)}
* and the two {@code removeNotificationListener} methods from {@link
* NotificationEmitter} as the corresponding {@code unsubscribe} methods
* from this class.</p>
*
* @param name The name of the MBean whose notifications are being
* subscribed, or unsuscribed.
*
* @return A {@link NotificationEmitter}
* that can be used to subscribe or unsubscribe for
* notifications emitted by the named MBean, or {@code null} if
* the MBean does not emit notifications and should not
* be considered as a {@code NotificationBroadcaster}. This class
* never returns null but a subclass is allowed to.
*
* @throws InstanceNotFoundException if {@code name} does not exist.
* This implementation never throws {@code InstanceNotFoundException} but
* a subclass is allowed to override this method to do so.
*/
public NotificationEmitter
getNotificationEmitterFor(final ObjectName name)
throws InstanceNotFoundException {
final NotificationEmitter emitter = new NotificationEmitter() {
public void addNotificationListener(NotificationListener listener,
NotificationFilter filter, Object handback)
throws IllegalArgumentException {
subscribe(name, listener, filter, handback);
}
public void removeNotificationListener(
NotificationListener listener)
throws ListenerNotFoundException {
unsubscribe(name, listener);
}
public void removeNotificationListener(NotificationListener listener,
NotificationFilter filter,
Object handback)
throws ListenerNotFoundException {
unsubscribe(name, listener, filter, handback);
}
public MBeanNotificationInfo[] getNotificationInfo() {
// Never called.
return null;
}
};
return emitter;
}
// ---------------------------------
// private stuff
// ---------------------------------
private static class ListenerInfo {
public final NotificationListener listener;
public final NotificationFilter filter;
public final Object handback;
public ListenerInfo(NotificationListener listener,
NotificationFilter filter,
Object handback) {
if (listener == null) {
throw new IllegalArgumentException("Null listener.");
}
this.listener = listener;
this.filter = filter;
this.handback = handback;
}
/* Two ListenerInfo instances are equal if they have the same
* NotificationListener. This means that we can use List.remove
* to implement the two-argument removeNotificationListener.
*/
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof ListenerInfo)) {
return false;
}
return listener.equals(((ListenerInfo)o).listener);
}
/* Method that compares all four fields, appropriate for the
* four-argument removeNotificationListener.
*/
boolean equals(
NotificationListener listener,
NotificationFilter filter,
Object handback) {
return (this.listener == listener && same(this.filter, filter)
&& same(this.handback, handback));
}
private static boolean same(Object x, Object y) {
if (x == y)
return true;
if (x == null)
return false;
return x.equals(y);
}
@Override
public int hashCode() {
return listener.hashCode();
}
}
private static void sendNotif(List<ListenerInfo> listeners, Notification n) {
for (ListenerInfo li : listeners) {
if (li.filter == null ||
li.filter.isNotificationEnabled(n)) {
try {
li.listener.handleNotification(n, li.handback);
} catch (Exception e) {
logger.trace("sendNotif", "handleNotification", e);
}
}
}
}
// ---------------------------------
// private variables
// ---------------------------------
private final Map<ObjectName, List<ListenerInfo>> exactSubscriptionMap =
new HashMap<ObjectName, List<ListenerInfo>>();
private final Map<ObjectName, List<ListenerInfo>> patternSubscriptionMap =
new HashMap<ObjectName, List<ListenerInfo>>();
// trace issue
private static final ClassLogger logger =
new ClassLogger("javax.management.event", "EventManager");
}

View file

@ -0,0 +1,597 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/**
* <p>The <code>javax.management.namespace</code> package makes it possible
* to federate MBeanServers into a hierarchical name space.</p>
*
* <h3 id="WhatIs">What Is a Name Space?</h3>
* <p>
* A name space is like an {@link javax.management.MBeanServer} within
* an {@code MBeanServer}. Just as a file system folder can contain
* another file system folder, an {@code MBeanServer} can contain another
* {@code MBeanServer}. Similarly, just as a remote folder on a remote
* disk can be mounted on a parent folder on a local disk, a remote name
* space in a remote {@code MBeanServer} can be mounted on a name
* space in a local parent {@code MBeanServer}.
* </p>
* <p>
* The <code>javax.management.namespace</code> API thus makes it possible to
* create a hierarchy of MBean servers federated in a hierarchical name
* space inside a single {@code MBeanServer}.
* </p>
* <h3 id="HowToCreate">How To Create a Name Space?</h3>
* <p>
* To create a name space, you only need to register a
* {@link javax.management.namespace.JMXNamespace} MBean in
* an MBean server. We have seen that a namespace is like
* an {@code MBeanServer} within an {@code MBeanServer}, and
* therefore, it is possible to create a namespace that shows the
* content of another {@code MBeanServer}. The simplest case is
* when that {@code MBeanServer} is another {@code MBeanServer}
* created by the {@link javax.management.MBeanServerFactory} as
* shown in the extract below:
* </p>
* <pre>
* final MBeanServer server = ....;
* final String namespace = "foo";
* final ObjectName namespaceName = {@link javax.management.namespace.JMXNamespaces#getNamespaceObjectName
* JMXNamespaces.getNamespaceObjectName(namespace)};
* server.registerMBean(new JMXNamespace(MBeanServerFactory.newMBeanServer()),
* namespaceName);
* </pre>
* <p id="NamespaceView">
* To navigate in namespaces and view their content, the easiest way is
* to use an instance of {@link javax.management.namespace.JMXNamespaceView}. For instance, given
* the {@code server} above, in which we created a namespace {@code "foo"},
* it is possible to create a {@code JMXNamespaceView} that will make it
* possible to navigate easily in the namespaces and sub-namespaces of that
* server:
* </p>
* <pre>
* // create a namespace view for 'server'
* final JMXNamespaceView view = new JMXNamespaceView(server);
*
* // list all top level namespaces in 'server'
* System.out.println("List of namespaces: " + Arrays.toString({@link javax.management.namespace.JMXNamespaceView#list() view.list()}));
*
* // go down into namespace 'foo': provides a namespace view of 'foo' and its
* // sub namespaces...
* final JMXNamespaceView foo = {@link javax.management.namespace.JMXNamespaceView#down view.down("foo")};
*
* // list all MBeans contained in namespace 'foo'
* System.out.println({@link javax.management.namespace.JMXNamespaceView#where() foo.where()} + " contains: " +
* {@link javax.management.namespace.JMXNamespaceView#getMBeanServerConnection foo.getMBeanServerConnection()}.queryNames(null,null));
* </pre>
* <p>
* It is also possible to create more complex namespaces, such as namespaces
* that point to MBean servers located in remote JVMs.
* </p>
* <p>
* For instance, to mount the MBeanServer accessible
* at <code>service:jmx:rmi:///jndi/rmi://localhost:9000/jmxrmi</code>
* in a name space {@code "foo"} inside the {@linkplain
* java.lang.management.ManagementFactory#getPlatformMBeanServer platform
* MBeanServer} you would write the following piece of code:
* </p>
* <pre>
* final JMXServiceURL sourceURL =
* new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9000/jmxrmi");
* final MBeanServer platform = ManagementFactory.getPlatformMBeanServer();
* final Map&lt;String,Object&gt; options = Collections.emptyMap();
* final JMXRemoteNamespace mbean = {@link
* javax.management.namespace.JMXRemoteNamespace JMXRemoteNamespace}.
* {@link javax.management.namespace.JMXRemoteNamespace#newJMXRemoteNamespace newJMXRemoteNamespace(sourceURL, options)};
* final ObjectName name = {@link javax.management.namespace.JMXNamespaces JMXNamespaces}.{@link javax.management.namespace.JMXNamespaces#getNamespaceObjectName(String) getNamespaceObjectName("foo")};
* final ObjectInstance ref = platform.registerMBean(mbean,name);
* platform.invoke(ref.getObjectName(),"connect",null,null);
* </pre>
*
* <h3 id="WhatLike">What Does a Name Space Look Like?</h3>
*
* <p>
* We have seen that {@link javax.management.namespace.JMXNamespaceView} class
* provides an easy way to navigate within namespaces. It is however also
* possible to interact with namespaces directly from the top level
* {@code MBeanServer} in which they have been created.
* From the outside, a name space only appears as a special MBean in
* the MBean server. There's nothing much you can do with this MBean
* directly.
* </p>
* <p>
* For instance, let's assume you have registered a {@link
* javax.management.namespace.JMXRemoteNamespaceMBean
* JMXRemoteNamespaceMBean} to manage the name space {@code "foo"}.
* <br>If you query for
* <code>platform.queryNames("&#42;//:*",null)</code>, then you will see
* one MBean named {@code "foo//:type=JMXNamespace"}.
* <br>This is the {@link javax.management.namespace.JMXNamespace}
* MBean which is in charge of handling the namespace {@code "foo"}.
* </p>
* <p>
* In fact, name space handler MBeans are instances of
* the class {@link javax.management.namespace.JMXNamespace} - or
* instances of a subclass of that class.
* They have a special {@link javax.management.ObjectName} defined by
* {@link javax.management.namespace.JMXNamespaces#getNamespaceObjectName
* JMXNamespaces.getNamespaceObjectName}.<br>
* {@link javax.management.namespace.JMXNamespace} instances are able
* to return an {@link
* javax.management.namespace.JMXNamespace#getSourceServer MBeanServer}
* which corresponds to the MBeanServer within (= the name space itself).
* </p>
* <p>
* So how does it work? How can you see the MBeans contained in the new
* name space?
* </p>
* <p>In order to address scalability issues, MBeans registered in
* namespaces (such as our namespace {@code "foo"} above) can not be
* seen with {@code mbeanServer.queryNames("*:*",null)}. To see the MBeans
* contained in a namespace, you can use one of these methods:
* </p>
* <ol>
* <li>
* You can use the {@link javax.management.namespace.JMXNamespaceView}
* class <a href="#NamespaceView">shown above</a>,
* </li>
* <li>
* or you can <a href="#NamespacePrefix">directly look</a> for MBeans
* whose names match
* {@code "foo//*:*"},
* </li>
* <li>
* or you can <a href="#ChangeTo">narrow down</a> to the namespace
* and obtain an MBeanServer
* proxy that corresponds to an MBeanServer view of that namespace.
* The JMXNamespaces class provides a static method that
* allows you to narrow down to a name space, by calling
* {@link javax.management.namespace.JMXNamespaces#narrowToNamespace(MBeanServer,String)
* JMXNamespaces.narrowToNamespace}.
* </li>
* </ol>
*
* <h3 id="NamespacePrefix">Using Name Space Prefixes</h3>
* <p>
* As we have explained above, MBeans contained in name
* spaces are not returned by {@code server.queryNames(null,null)} - or
* <code>server.queryNames({@link javax.management.ObjectName#WILDCARD ObjectName.WILDCARD},null)</code>.
* <br>
* However, these MBeans can still be accessed from the top level
* {@code MBeanServer} interface, without using any API specific to the
* version 2.0 of the JMX API, simply by using object names with
* name space prefixes:
* <br>To list MBeans contained in a namespace {@code "foo"} you can
* query for MBeans whose names match {@code "foo//*:*"}, as shown
* earlier in this document:
* <pre>
* server.queryNames(new ObjectName("foo//*:*", null);
* // or equivalently:
* server.queryNames(JMXNamespaces.getWildcardFor("foo"), null);
* </pre>
* This will return a list of MBean names whose domain name starts
* with {@code foo//}.
* </p><p>
* Using these names, you can invoke any operation on the corresponding
* MBeans. For instance, to get the {@link javax.management.MBeanInfo
* MBeanInfo} of an MBean
* contained in name space {@code "foo"} (assuming
* the name of the MBean within its name space is <i>domain:type=Thing</i>,
* then simply call:
* <pre>
* server.getMBeanInfo(new ObjectName("foo//domain:type=Thing"));
* </pre>
* An easier way to access MBeans contained in a name space is to
* <i>cd</i> inside the name space, as shown in the following paragraph.
* </p>
*
* <h3 id="ChangeTo">Narrowing Down Into a Name Spaces</h3>
* <p>
* As we have seen, name spaces are like MBean servers within MBean servers.
* Therefore, it is possible to view a name space just as if it were
* an other MBean server. This is similar to opening a sub
* folder from a parent folder.<br>
* This operation is illustrated in the code extract below:
* <pre>
* final MBeanServer foo =
* JMXNamespaces.narrowToNamespace(platform, "foo");
* final MBeanInfo info =
* foo.getMBeanInfo(new ObjectName("domain:type=Thing"));
* </pre>
* The {@code MBeanServer} returned by {@link
* javax.management.namespace.JMXNamespaces#narrowToNamespace(MBeanServer,String)
* JMXNamespaces.narrowToNamespace} is an {@code MBeanServer} view that
* narrows down into a given namespace. The MBeans contained inside that
* namespace can now be accessed by their regular local name. <br>
* The MBean server obtained by narrowing down
* to name space {@code "foo"} behaves just like a regular MBean server.
* However, it may sometimes throw an {@link
* java.lang.UnsupportedOperationException UnsupportedOperationException}
* wrapped in a JMX exception if you try to call an operation which is not
* supported by the underlying name space handler.
* <br>For instance, {@link javax.management.MBeanServer#registerMBean
* registerMBean} is not supported for name spaces mounted from remote
* MBean servers.
* </p>
* <p>
* <u>Note:</u> If you have a deep hierarchy of namespaces, and if you
* are switching from one namespace to another in the course of your
* application, it might be more convenient to use a
* {@link javax.management.namespace.JMXNamespaceView}
* in order to navigate in your namespaces.
* </p>
*
* <h3 id="NamespaceTypes">Different Types of Name Spaces</h3>
* <p>
* This API lets you create several types of name spaces:
* <ul>
* <li id="RemoteNS">
* You can use the {@link
* javax.management.namespace.JMXRemoteNamespace
* JMXRemoteNamespace} to create
* <b>remote</b> name spaces, mounted from
* a remote sub {@code MBeanServer} source, as shown
* <a href="#HowToCreate">earlier</a> in this document.
* </li>
* <li id="LocalNS">
* You can also use {@link
* javax.management.namespace.JMXNamespace
* JMXNamespace} to create
* <b>local</b> name spaces,
* by providing a direct reference to another {@code MBeanServer}
* instance living in the same JVM.
* </li>
* <li id="VirtualNS">
* Finally, you can create
* name spaces containing <b>virtual</b> MBeans,
* by subclassing the {@link
* javax.management.namespace.MBeanServerSupport
* MBeanServerSupport}, and passing an instance of
* your own subclass to a {@link
* javax.management.namespace.JMXNamespace JMXNamespace}.
* </li>
* <li id="CustomNS">
* If none of these classes suit your needs, you can also provide
* <b>your own</b> subclass of {@link
* javax.management.namespace.JMXNamespace
* JMXNamespace}. This is however discouraged.
* </li>
* </ul>
* </p>
*
* <h3 id="SpecialOp">Name Spaces And Special Operations</h3>
* <p>
* MBean Naming considerations aside, Name Spaces are transparent for
* most {@code MBeanServer} operations. There are however a few
* exceptions:
* </p>
* <ul>
* <li>
* <p>MBeanServer only operations - these are the operations which are
* supported by {@link javax.management.MBeanServer MBeanServer} but
* are not present in {@link
* javax.management.MBeanServerConnection
* MBeanServerConnection}. Since a name space can be a local view of
* a remote {@code MBeanServer}, accessible only through an
* {@code MBeanServerConnection}, these
* kinds of operations are not always supported.</p>
* <ul>
* <li id="registerMBean">
* <p>registerMBean:</p>
* <p> The {@link javax.management.MBeanServer#registerMBean
* registerMBean}
* operation is not supported by most name spaces. A call
* to
* <pre>
* MBeanServer server = ....;
* ThingMBean mbean = new Thing(...);
* ObjectName name = new ObjectName("foo//domain:type=Thing");
* server.registerMBean(mbean, name);
* </pre>
* will usually fail, unless the name space
* {@code "foo"} is a <a href="#LocalNS">local</a> name
* space. In the case where you attempt to cross
* multiple name spaces, then all name spaces in the
* path must support the {@code registerMBean} operation
* in order for it to succeed.<br>
* To create an MBean inside a name space, it is
* usually safer to use {@code createMBean} -
* although some <a href="#MBeanCreation">special
* considerations</a> can also apply.
* </p>
* <p></p>
* </li>
* <li id="getClassLoader">
* <p>getClassLoader:</p>
* <p> Similarly to <a href="#registerMBean">registerMBean</a>,
* and for the same reasons, {@link
* javax.management.MBeanServer#getClassLoader
* getClassLoader} will usually fail, unless the
* class loader is an MBean registered in a
* <a href="#LocalNS">local</a> name space.<br>
* </p>
* </li>
* <li id="getClassLoaderFor">
* <p>getClassLoaderFor:</p>
* <p> The implementation of {@link
* javax.management.MBeanServer#getClassLoaderFor
* getClassLoaderFor} also depends on which
* <a href="#NamespaceTypes">type of name space</a>
* handler is used across the namespace path.
* </p>
* <p>
* A <a href="#LocalNS">local</a> name space will usually
* be able to implement this method just as a real
* {@code MBeanServer} would. A
* <a href="#RemoteNS">remote</a> name space will usually
* return the default class loader configured on the
* internal {@link javax.management.remote.JMXConnector
* JMXConnector} used to connect to the remote server.
* When a {@link
* javax.management.namespace.JMXRemoteNamespace
* JMXRemoteNamespace} is used to connect to a
* remote server that contains MBeans which export
* custom types, the {@link
* javax.management.namespace.JMXRemoteNamespace
* JMXRemoteNamespace} must thus be configured with
* an options map such that the underlying connector
* can obtain a default class loader able
* to handle those types.
* </p>
* <p>
* Other <a href="#NamespaceTypes">types of name spaces</a>
* may implement this method
* as best as they can.
* </p>
* </li>
* </ul>
* </li>
* <li id="MBeanCreation">
* <p>MBean creation</p>
* <p> MBean creation through {@link
* javax.management.MBeanServerConnection#createMBean
* createMBean} might not be supported by all
* name spaces: <a href="#LocalNS">local</a> name spaces and
* <a href="#LocalNS">remote</a> name spaces will usually
* support it, but <a href="#VirtualNS">virtual</a> name
* spaces and <a href="#CustomNS">custom</a> name
* spaces might not.
* </p>
* <p>
* In that case, they will throw an {@link
* java.lang.UnsupportedOperationException
* UnsupportedOperationException} usually wrapped into an {@link
* javax.management.MBeanRegistrationException}.
* </p>
* </li>
* <li id="Notifications">
* <p>Notifications</p>
* <p> Some namespaces might not support JMX Notifications. In that
* case, a call to add or remove notification listener for an
* MBean contained in that name space will raise a
* {@link javax.management.RuntimeOperationsException
* RuntimeOperationsException} wrapping an {@link
* java.lang.UnsupportedOperationException
* UnsupportedOperationException} exception.
* </p>
* </li>
* </ul>
*
* <h3 id="CrossingNamespace">Crossing Several Name Spaces</h3>
* <p>
* Just as folders can contain other folders, name spaces can contain
* other name spaces. For instance, if an {@code MBeanServer} <i>S1</i>
* containing a name space {@code "bar"} is mounted in another
* {@code MBeanServer} <i>S2</i> with name space {@code "foo"}, then
* an MBean <i>M1</i> named {@code "domain:type=Thing"} in namespace
* {@code "bar"} will appear as {@code "foo//bar//domain:type=Thing"} in
* {@code MBeanServer} <i>S2</i>.
* </p>
* <p>
* When accessing the MBean <i>M1</i> from server <i>S2</i>, the
* method call will traverse in a cascade {@code MBeanServer} <i>S2</i>,
* then the name space handler for name space {@code "foo"}, then
* {@code MBeanServer} <i>S1</i>, before coming to the name space
* handler for name space {@code "bar"}. Any operation invoked
* on the MBean from a "top-level" name space will therefore need to
* traverse all the name spaces along the name space path until
* it eventually reaches the named MBean. This means that an operation
* like <a href="#registerMBean">registerMBean</a> for instance,
* can only succeed if all the name spaces along the path support it.
* </p>
* <p>
* Narrowing to a nested name space works just the same as narrowing
* to a top level name space:
* <pre>
* final MBeanServer S2 = .... ;
* final MBeanServer bar =
* JMXNamespaces.narrowToNamespace(S2, "foo//bar");
* final MBeanInfo info =
* foo.getMBeanInfo(new ObjectName("domain:type=Thing"));
* </pre>
* </p>
*
* <h3 id="OperationResult">Name Spaces And Operation Results</h3>
* <p>
* Operation results, as well as attribute values returned by an MBean
* contained in a name space must be interpreted in the context of that
* name space.<br>
* In other words, if an MBean in name space "foo" has an attribute of
* type {@code ObjectName}, then it must be assumed that the
* {@code ObjectName} returned by that MBean is relative to
* name space "foo".<br>
* The same rule aplies for MBean names that can be returned by
* operations invoked on such an MBean. If one of the MBean operations
* return, say, a {@code Set<ObjectName>} then those MBean names must
* also be assumed to be relative to name space "foo".<br>
* </p>
* <p>
* In the usual case, a JMX client will first
* <a href="#ChangeTo">narrow to a name space</a> before invoking
* any operation on the MBeans it contains. In that case the names
* returned by the MBean invoked can be directly fed back to the
* narrowed connection.
* <br>
* If however, the JMX client directly invoked the MBean from a higher
* name space, without having narrowed to that name space first, then
* the names that might be returned by that MBean will not be directly
* usable - the JMX client will need to either
* <a href="#ChangeTo">narrow to the name space</a> before using the
* returned names, or convert the names to the higher level name space
* context.
* <br>
* The {@link javax.management.namespace.JMXNamespaces JMXNamespaces}
* class provides methods that can be used to perform that conversion.
* </p>
*
* <h3 id="NamespacesAndNotifications">Name Spaces And Notifications</h3>
* <p>
* As <a href="#WhatIs">already explained</a>, name spaces are very
* similar to {@code MBeanServer}s. It is thus possible to get
* {@link javax.management.MBeanServerNotification MBeanServerNotifications}
* when MBeans are added or removed within a name space, by registering
* with the {@link javax.management.MBeanServerDelegate
* MBeanServerDelegate} MBean of the corresponding name space.<br>
* However, it must be noted that the notifications emitted by a
* name space must be interpreted in the context of that name space.
* For instance, if an MBean {@code "domain:type=Thing"} contained in
* namespace "foo//bar" emits a notification, the source of the
* notification will be {@code "domain:type=Thing"}, not
* {@code "foo//bar//domain:type=Thing"}. <br>
* It is therefore recommended to keep track of the name space
* information when registering a listener with an MBean contained in
* a name space, especially if the same listener is used to receive
* notifications from different name spaces. An easy solution is to
* use the handback, as illustrated in the code below.
* <pre>
* final MBeanServer server = ...;
* final NotificationListener listener = new NotificationListener() {
* public void handleNotification(Notification n, Object handback) {
* if (!(n instanceof MBeanServerNotification)) {
* System.err.println("Error: expected MBeanServerNotification");
* return;
* }
* final MBeanServerNotification mbsn =
* (MBeanServerNotification) n;
*
* // We will pass the namespace path in the handback.
* //
* // The received notification must be interpreted in
* // the context of its source - therefore
* // mbsn.getMBeanName() does not include the name space
* // path...
* //
* final String namespace = (String) handback;
* System.out.println("Received " + mbsn.getType() +
* " for MBean " + mbsn.getMBeanName() +
* " from name space " + namespace);
* }
* };
* server.addNotificationListener(JMXNamespaces.insertPath("foo//bar",
* MBeanServerDelegate.DELEGATE_NAME),listener,null,"foo//bar");
* server.addNotificationListener(JMXNamespaces.insertPath("foo//joe",
* MBeanServerDelegate.DELEGATE_NAME),listener,null,"foo//joe");
* </pre>
* </p>
* <p>
* JMX Connectors may require some configuration in order to be able
* to forward notifications from MBeans located in name spaces.
* The RMI JMX Connector Server
* in the Java SE 7 platform is configured by default to internally
* use the new {@linkplain javax.management.event event service} on
* the server side.
* When the connector server is configured in this way, JMX clients
* which use the old JMX Notifications mechanism (such as clients
* running on prior versions of the JDK) will be able to
* to receive notifications from MBeans located in sub name spaces.
* This is because the connector server will transparently delegate
* their subscriptions to the underlying {@linkplain
* javax.management.event event service}. In summary:
* <ul>
* <li>
* On the server side: When exporting an {@code MBeanServer}
* through a JMX Connector, you will need to make sure that the
* connector server uses the new {@linkplain javax.management.event
* event service} in order to register for notifications. If the
* connector server doesn't use the event service, only clients
* which explicitly use the new {@linkplain javax.management.event
* event service} will be able to register for notifications
* with MBeans located in sub name spaces.
* </li>
* <li>
* On the client side: if the JMX Connector server (on the remote
* server side) was configured to internally use the new
* {@linkplain javax.management.event
* event service}, then clients can continue to use the old
* {@code MBeanServerConnection} add / remove notification
* listener methods transparently. Otherwise, only clients which
* explicitly use the new {@linkplain javax.management.event
* event service} will be able to receive notifications from
* MBeans contained in sub name spaces.
* </li>
* </ul>
* </p>
* <p>
* These configuration issues apply at each node in the name space path,
* whenever the name space points to a remote server. The
* {@link javax.management.namespace.JMXRemoteNamespace
* JMXRemoteNamespace} can be configured in such a way that it will
* explicitly use an {@link javax.management.event.EventClient EventClient}
* when forwarding subscription to the remote side. Note that this can be
* unnecessary (and a waste of resources) if the underlying JMXConnector
* returned by the JMXConnectorFactory (client side) already uses the
* {@linkplain javax.management.event event service} to register for
* notifications with the server side.
* </p>
*
* <h3 id="Security">Name Spaces And Access Control</h3>
* <p>
* Access to MBeans exposed through JMX namespaces is controlled by
* {@linkplain javax.management.namespace.JMXNamespacePermission
* jmx namespace permissions}. These permissions are checked by the
* MBeanServer in which the {@link
* javax.management.namespace.JMXNamespace JMXNamespace} MBean is registered.
* This is <a href="JMXNamespace.html#PermissionChecks">described in
* details</a> in the {@link
* javax.management.namespace.JMXNamespace JMXNamespace} class.
* </p>
* <p>
* To implement a "firewall-like" access control in a JMX agent you
* can also place an {@link
* javax.management.remote.MBeanServerForwarder} in the JMX Connector
* Server which exposes the top-level MBeanServer of your application.
* This {@code MBeanServerForwarder} will be able to perform
* authorization checks for all MBeans, including those located in
* sub name spaces.
* </p>
* <p>
* For a tighter access control we recommend using a {@link
* java.lang.SecurityManager security manager}.
* </p>
* @since 1.7
* <p></p>
**/
package javax.management.namespace;

View file

@ -89,7 +89,7 @@ public class CompositeType extends OpenType<CompositeData> {
* <br>&nbsp;
* @param itemNames The names of the items contained in the
* composite data values described by this <code>CompositeType</code> instance;
* cannot be null and should contain at least one element; no element can be a null or empty string.
* cannot be null; no element can be null or an empty string.
* Note that the order in which the item names are given is not important to differentiate a
* <code>CompositeType</code> instance from another;
* the item names are internally stored sorted in ascending alphanumeric order.
@ -97,7 +97,7 @@ public class CompositeType extends OpenType<CompositeData> {
* @param itemDescriptions The descriptions, in the same order as <var>itemNames</var>, of the items contained in the
* composite data values described by this <code>CompositeType</code> instance;
* should be of the same size as <var>itemNames</var>;
* no element can be a null or empty string.
* no element can be null or an empty string.
* <br>&nbsp;
* @param itemTypes The open type instances, in the same order as <var>itemNames</var>, describing the items contained
* in the composite data values described by this <code>CompositeType</code> instance;
@ -125,7 +125,7 @@ public class CompositeType extends OpenType<CompositeData> {
//
super(CompositeData.class.getName(), typeName, description, false);
// Check the 3 arrays are not null or empty (ie length==0) and that there is no null element or empty string in them
// Check the 3 arrays are not null and that there is no null element or empty string in them
//
checkForNullElement(itemNames, "itemNames");
checkForNullElement(itemDescriptions, "itemDescriptions");

View file

@ -268,6 +268,14 @@ public class JMXConnectorFactory {
return conn;
}
private static <K,V> Map<K,V> newHashMap() {
return new HashMap<K,V>();
}
private static <K> Map<K,Object> newHashMap(Map<K,?> map) {
return new HashMap<K,Object>(map);
}
/**
* <p>Creates a connector client for the connector server at the
* given address. The resultant client is not connected until its
@ -300,16 +308,18 @@ public class JMXConnectorFactory {
public static JMXConnector newJMXConnector(JMXServiceURL serviceURL,
Map<String,?> environment)
throws IOException {
Map<String, Object> envcopy;
final Map<String,Object> envcopy;
if (environment == null)
envcopy = new HashMap<String, Object>();
envcopy = newHashMap();
else {
EnvHelp.checkAttributes(environment);
envcopy = new HashMap<String, Object>(environment);
envcopy = newHashMap(environment);
}
final ClassLoader loader = resolveClassLoader(envcopy);
final Class<JMXConnectorProvider> targetInterface = JMXConnectorProvider.class;
final Class<JMXConnectorProvider> targetInterface =
JMXConnectorProvider.class;
final String protocol = serviceURL.getProtocol();
final String providerClassName = "ClientProvider";
@ -351,9 +361,10 @@ public class JMXConnectorFactory {
}
}
envcopy = Collections.unmodifiableMap(envcopy);
final Map<String,Object> fixedenv =
Collections.unmodifiableMap(envcopy);
return provider.newJMXConnector(serviceURL, envcopy);
return provider.newJMXConnector(serviceURL, fixedenv);
}
private static String resolvePkgs(Map env) throws JMXProviderException {
@ -365,8 +376,8 @@ public class JMXConnectorFactory {
if (pkgsObject == null)
pkgsObject =
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty(PROTOCOL_PROVIDER_PACKAGES);
}
});
@ -423,8 +434,7 @@ public class JMXConnectorFactory {
static <T> Iterator<T> getProviderIterator(final Class<T> providerClass,
final ClassLoader loader) {
ServiceLoader<T> serviceLoader =
ServiceLoader.load(providerClass,
loader);
ServiceLoader.load(providerClass, loader);
return serviceLoader.iterator();
}
@ -528,8 +538,8 @@ public class JMXConnectorFactory {
}
if (loader == null)
loader =
AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
loader = AccessController.doPrivileged(
new PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return
Thread.currentThread().getContextClassLoader();

View file

@ -30,13 +30,14 @@ package javax.management.remote;
import com.sun.jmx.remote.util.ClassLogger;
import com.sun.jmx.remote.util.EnvHelp;
import java.beans.ConstructorProperties;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.util.BitSet;
import java.util.StringTokenizer;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.InvalidKeyException;
/**
* <p>The address of a JMX API connector server. Instances of this class
@ -273,7 +274,6 @@ public class JMXServiceURL implements Serializable {
* is not possible to find the local host name, or if
* <code>port</code> is negative.
*/
@ConstructorProperties({"protocol", "host", "port", "URLPath"})
public JMXServiceURL(String protocol, String host, int port,
String urlPath)
throws MalformedURLException {
@ -338,6 +338,50 @@ public class JMXServiceURL implements Serializable {
validate();
}
/**
* <p>Construct a {@code JMXServiceURL} instance from the given
* {@code CompositeData}. The presence of this method means that
* {@code JMXServiceURL} is <a href="../MXBean.html#reconstructible-def">
* reconstructible</a> in MXBeans.</p>
*
* <p>(The effect of this method could have been obtained more simply
* with a &#64;{@link java.beans.ConstructorProperties ConstructorProperties}
* annotation on the four-parameter {@linkplain #JMXServiceURL(
* String, String, int, String) constructor}, but that would have meant
* that this API could not be implemented on versions of the Java platform
* that predated the introduction of that annotation.)</p>
*
* @param cd a {@code CompositeData} object that must contain items called
* {@code protocol}, {@code host}, and {@code URLPath} of type {@code String}
* and {@code port} of type {@code Integer}. Such an object will be produced
* by the MXBean framework when <a
* href="../MXBean.html#composite-map">mapping</a> a {@code JMXServiceURL}
* instance to an Open Data value.
*
* @return a {@code JMXServiceURL} constructed with the protocol, host,
* port, and URL path extracted from the given {@code CompositeData}.
*
* @throws MalformedURLException if the given {@code CompositeData} does
* not contain all the required items with the required types or if the
* resultant URL is syntactically incorrect.
*/
public static JMXServiceURL from(CompositeData cd)
throws MalformedURLException {
try {
String proto = (String) cd.get("protocol");
String host = (String) cd.get("host");
int port = (Integer) cd.get("port");
String urlPath = (String) cd.get("URLPath");
return new JMXServiceURL(proto, host, port, urlPath);
} catch (RuntimeException e) {
// Could be InvalidKeyException if the item is missing,
// or ClassCastException if it is present but with the wrong type.
MalformedURLException x = new MalformedURLException(e.getMessage());
x.initCause(e);
throw x;
}
}
private void validate() throws MalformedURLException {
// Check protocol

View file

@ -77,6 +77,7 @@ import javax.management.event.EventClientDelegate;
import javax.management.event.EventClientDelegateMBean;
import javax.management.event.EventClientNotFoundException;
import javax.management.event.FetchingEventForwarder;
import javax.management.namespace.JMXNamespaces;
import javax.management.remote.JMXServerErrorException;
import javax.management.remote.NotificationResult;
import javax.management.remote.TargetedNotification;
@ -1292,11 +1293,27 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
public void removeNotificationListener(ObjectName name, Integer id)
throws InstanceNotFoundException, ListenerNotFoundException,
IOException {
if (!JMXNamespaces.getContainingNamespace(name).equals("")) {
logger.debug("removeNotificationListener",
"This connector server is not configured to support " +
"forwarding of notification subscriptions to name spaces");
throw new RuntimeOperationsException(
new UnsupportedOperationException(
"removeNotificationListener on name space MBeans. "));
}
forwarder.removeNotificationListener(name,id);
}
public void removeNotificationListener(ObjectName name, Integer[] ids)
throws Exception {
if (!JMXNamespaces.getContainingNamespace(name).equals("")) {
logger.debug("removeNotificationListener",
"This connector server is not configured to support " +
"forwarding of notification subscriptions to name spaces");
throw new RuntimeOperationsException(
new UnsupportedOperationException(
"removeNotificationListener on name space MBeans. "));
}
forwarder.removeNotificationListener(name,ids);
}
@ -1307,6 +1324,14 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
public Integer addNotificationListener(ObjectName name,
NotificationFilter filter)
throws InstanceNotFoundException, IOException {
if (!JMXNamespaces.getContainingNamespace(name).equals("")) {
logger.debug("addNotificationListener",
"This connector server is not configured to support " +
"forwarding of notification subscriptions to name spaces");
throw new RuntimeOperationsException(
new UnsupportedOperationException(
"addNotificationListener on name space MBeans. "));
}
return forwarder.addNotificationListener(name,filter);
}
@ -1326,6 +1351,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
private final boolean checkNotificationEmission;
private final String clientId;
private final String connectionId;
private volatile String mbeanServerName;
EventSubscriptionManager(
MBeanServer mbeanServer,
@ -1343,6 +1369,11 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
this.connectionId = connectionId;
}
private String mbeanServerName() {
if (mbeanServerName != null) return mbeanServerName;
else return (mbeanServerName = getMBeanServerName(mbeanServer));
}
@SuppressWarnings("serial") // no serialVersionUID
private class AccessControlFilter implements NotificationFilter {
private final NotificationFilter wrapped;
@ -1357,7 +1388,8 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
try {
if (checkNotificationEmission) {
ServerNotifForwarder.checkMBeanPermission(
mbeanServer, name, "addNotificationListener");
mbeanServerName(), mbeanServer, name,
"addNotificationListener");
}
notifAC.fetchNotification(
connectionId, name, notification, getSubject());
@ -1867,6 +1899,15 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
return e;
}
private static String getMBeanServerName(final MBeanServer server) {
final PrivilegedAction<String> action = new PrivilegedAction<String>() {
public String run() {
return Util.getMBeanServerSecurityName(server);
}
};
return AccessController.doPrivileged(action);
}
private static final Object[] NO_OBJECTS = new Object[0];
private static final String[] NO_STRINGS = new String[0];

View file

@ -31,7 +31,7 @@ import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.channels.spi.*;
import java.lang.ref.SoftReference;
import java.util.*;
/**
@ -47,11 +47,14 @@ class DatagramChannelImpl
private static NativeDispatcher nd = new DatagramDispatcher();
// Our file descriptor
FileDescriptor fd = null;
private final FileDescriptor fd;
// fd value needed for dev/poll. This value will remain valid
// even after the value in the file descriptor object has been set to -1
int fdVal;
private final int fdVal;
// The protocol family of the socket
private final ProtocolFamily family;
// IDs of native threads doing reads and writes, for signalling
private volatile long readerThread = 0;
@ -59,8 +62,8 @@ class DatagramChannelImpl
// Cached InetAddress and port for unconnected DatagramChannels
// used by receive0
private InetAddress cachedSenderInetAddress = null;
private int cachedSenderPort = 0;
private InetAddress cachedSenderInetAddress;
private int cachedSenderPort;
// Lock held by current reading or connecting thread
private final Object readLock = new Object();
@ -76,20 +79,20 @@ class DatagramChannelImpl
// State (does not necessarily increase monotonically)
private static final int ST_UNINITIALIZED = -1;
private static int ST_UNCONNECTED = 0;
private static int ST_CONNECTED = 1;
private static final int ST_UNCONNECTED = 0;
private static final int ST_CONNECTED = 1;
private static final int ST_KILLED = 2;
private int state = ST_UNINITIALIZED;
// Binding
private SocketAddress localAddress = null;
SocketAddress remoteAddress = null;
// Options
private SocketOpts.IP options = null;
private SocketAddress localAddress;
private SocketAddress remoteAddress;
// Our socket adaptor, if any
private DatagramSocket socket = null;
private DatagramSocket socket;
// Multicast support
private MembershipRegistry registry;
// -- End of fields protected by stateLock
@ -98,7 +101,26 @@ class DatagramChannelImpl
throws IOException
{
super(sp);
this.fd = Net.socket(false);
this.family = Net.isIPv6Available() ?
StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
this.fd = Net.socket(family, false);
this.fdVal = IOUtil.fdVal(fd);
this.state = ST_UNCONNECTED;
}
public DatagramChannelImpl(SelectorProvider sp, ProtocolFamily family) {
super(sp);
if ((family != StandardProtocolFamily.INET) &&
(family != StandardProtocolFamily.INET6)) {
throw new UnsupportedOperationException("Protocol family not supported");
}
if (family == StandardProtocolFamily.INET6) {
if (!Net.isIPv6Available()) {
throw new UnsupportedOperationException("IPv6 not available");
}
}
this.family = family;
this.fd = Net.socket(family, false);
this.fdVal = IOUtil.fdVal(fd);
this.state = ST_UNCONNECTED;
}
@ -107,9 +129,12 @@ class DatagramChannelImpl
throws IOException
{
super(sp);
this.family = Net.isIPv6Available() ?
StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
this.fd = fd;
this.fdVal = IOUtil.fdVal(fd);
this.state = ST_UNCONNECTED;
this.localAddress = Net.localAddress(fd);
}
public DatagramSocket socket() {
@ -120,6 +145,156 @@ class DatagramChannelImpl
}
}
@Override
public SocketAddress getLocalAddress() throws IOException {
synchronized (stateLock) {
if (!isOpen())
return null;
return localAddress;
}
}
@Override
public SocketAddress getConnectedAddress() throws IOException {
synchronized (stateLock) {
if (!isOpen())
return null;
return remoteAddress;
}
}
@Override
public DatagramChannel setOption(SocketOption name, Object value)
throws IOException
{
if (name == null)
throw new NullPointerException();
if (!options().contains(name))
throw new IllegalArgumentException("Invalid option name");
synchronized (stateLock) {
ensureOpen();
if (name == StandardSocketOption.IP_TOS) {
// IPv4 only; no-op for IPv6
if (family == StandardProtocolFamily.INET) {
Net.setSocketOption(fd, family, name, value);
}
return this;
}
if (name == StandardSocketOption.IP_MULTICAST_TTL ||
name == StandardSocketOption.IP_MULTICAST_LOOP)
{
// options are protocol dependent
Net.setSocketOption(fd, family, name, value);
return this;
}
if (name == StandardSocketOption.IP_MULTICAST_IF) {
if (value == null)
throw new IllegalArgumentException("Cannot set IP_MULTICAST_IF to 'null'");
NetworkInterface interf = (NetworkInterface)value;
if (family == StandardProtocolFamily.INET6) {
int index = interf.getIndex();
if (index == -1)
throw new IOException("Network interface cannot be identified");
Net.setInterface6(fd, index);
} else {
// need IPv4 address to identify interface
Inet4Address target = Net.anyInet4Address(interf);
if (target == null)
throw new IOException("Network interface not configured for IPv4");
int targetAddress = Net.inet4AsInt(target);
Net.setInterface4(fd, targetAddress);
}
return this;
}
// remaining options don't need any special handling
Net.setSocketOption(fd, Net.UNSPEC, name, value);
return this;
}
}
@Override
@SuppressWarnings("unchecked")
public <T> T getOption(SocketOption<T> name)
throws IOException
{
if (name == null)
throw new NullPointerException();
if (!options().contains(name))
throw new IllegalArgumentException("Invalid option name");
synchronized (stateLock) {
ensureOpen();
if (name == StandardSocketOption.IP_TOS) {
// IPv4 only; always return 0 on IPv6
if (family == StandardProtocolFamily.INET) {
return (T) Net.getSocketOption(fd, family, name);
} else {
return (T) Integer.valueOf(0);
}
}
if (name == StandardSocketOption.IP_MULTICAST_TTL ||
name == StandardSocketOption.IP_MULTICAST_LOOP)
{
return (T) Net.getSocketOption(fd, family, name);
}
if (name == StandardSocketOption.IP_MULTICAST_IF) {
if (family == StandardProtocolFamily.INET) {
int address = Net.getInterface4(fd);
if (address == 0)
return null; // default interface
InetAddress ia = Net.inet4FromInt(address);
NetworkInterface ni = NetworkInterface.getByInetAddress(ia);
if (ni == null)
throw new IOException("Unable to map address to interface");
return (T) ni;
} else {
int index = Net.getInterface6(fd);
if (index == 0)
return null; // default interface
NetworkInterface ni = NetworkInterface.getByIndex(index);
if (ni == null)
throw new IOException("Unable to map index to interface");
return (T) ni;
}
}
// no special handling
return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
}
}
private static class LazyInitialization {
static final Set<SocketOption<?>> defaultOptions = defaultOptions();
private static Set<SocketOption<?>> defaultOptions() {
HashSet<SocketOption<?>> set = new HashSet<SocketOption<?>>(8);
set.add(StandardSocketOption.SO_SNDBUF);
set.add(StandardSocketOption.SO_RCVBUF);
set.add(StandardSocketOption.SO_REUSEADDR);
set.add(StandardSocketOption.SO_BROADCAST);
set.add(StandardSocketOption.IP_TOS);
set.add(StandardSocketOption.IP_MULTICAST_IF);
set.add(StandardSocketOption.IP_MULTICAST_TTL);
set.add(StandardSocketOption.IP_MULTICAST_LOOP);
return Collections.unmodifiableSet(set);
}
}
@Override
public final Set<SocketOption<?>> options() {
return LazyInitialization.defaultOptions;
}
private void ensureOpen() throws ClosedChannelException {
if (!isOpen())
throw new ClosedChannelException();
@ -135,8 +310,10 @@ class DatagramChannelImpl
synchronized (readLock) {
ensureOpen();
// If socket is not bound then behave as if nothing received
if (!isBound()) // ## NotYetBoundException ??
// Will be fixed by 6621699
if (localAddress() == null) {
return null;
}
int n = 0;
ByteBuffer bb = null;
try {
@ -267,6 +444,12 @@ class DatagramChannelImpl
do {
n = send(fd, src, target);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
synchronized (stateLock) {
if (isOpen() && (localAddress == null)) {
localAddress = Net.localAddress(fd);
}
}
return IOStatus.normalize(n);
} finally {
writerThread = 0;
@ -316,7 +499,8 @@ class DatagramChannelImpl
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
int written = send0(fd, ((DirectBuffer)bb).address() + pos,
boolean preferIPv6 = (family != StandardProtocolFamily.INET);
int written = send0(preferIPv6, fd, ((DirectBuffer)bb).address() + pos,
rem, target);
if (written > 0)
bb.position(pos + written);
@ -453,42 +637,8 @@ class DatagramChannelImpl
IOUtil.configureBlocking(fd, block);
}
public SocketOpts options() {
synchronized (stateLock) {
if (options == null) {
SocketOptsImpl.Dispatcher d
= new SocketOptsImpl.Dispatcher() {
int getInt(int opt) throws IOException {
return Net.getIntOption(fd, opt);
}
void setInt(int opt, int arg)
throws IOException
{
Net.setIntOption(fd, opt, arg);
}
};
options = new SocketOptsImpl.IP(d);
}
return options;
}
}
public boolean isBound() {
return Net.localPortNumber(fd) != 0;
}
public SocketAddress localAddress() {
synchronized (stateLock) {
if (isConnected() && (localAddress == null)) {
// Socket was not bound before connecting,
// so ask what the address turned out to be
localAddress = Net.localAddress(fd);
}
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
InetSocketAddress isa = (InetSocketAddress)localAddress;
sm.checkConnect(isa.getAddress().getHostAddress(), -1);
}
return localAddress;
}
}
@ -499,22 +649,37 @@ class DatagramChannelImpl
}
}
public void bind(SocketAddress local) throws IOException {
@Override
public DatagramChannel bind(SocketAddress local) throws IOException {
synchronized (readLock) {
synchronized (writeLock) {
synchronized (stateLock) {
ensureOpen();
if (isBound())
if (localAddress != null)
throw new AlreadyBoundException();
InetSocketAddress isa = Net.checkAddress(local);
InetSocketAddress isa;
if (local == null) {
isa = new InetSocketAddress(0);
} else {
isa = Net.checkAddress(local);
// only Inet4Address allowed with IPv4 socket
if (family == StandardProtocolFamily.INET) {
InetAddress addr = isa.getAddress();
if (!(addr instanceof Inet4Address))
throw new UnsupportedAddressTypeException();
}
}
SecurityManager sm = System.getSecurityManager();
if (sm != null)
if (sm != null) {
sm.checkListen(isa.getPort());
Net.bind(fd, isa.getAddress(), isa.getPort());
}
Net.bind(family, fd, isa.getAddress(), isa.getPort());
localAddress = Net.localAddress(fd);
}
}
}
return this;
}
public boolean isConnected() {
@ -533,7 +698,6 @@ class DatagramChannelImpl
}
public DatagramChannel connect(SocketAddress sa) throws IOException {
int trafficClass = 0;
int localPort = 0;
synchronized(readLock) {
@ -545,10 +709,10 @@ class DatagramChannelImpl
if (sm != null)
sm.checkConnect(isa.getAddress().getHostAddress(),
isa.getPort());
int n = Net.connect(fd,
int n = Net.connect(family,
fd,
isa.getAddress(),
isa.getPort(),
trafficClass);
isa.getPort());
if (n <= 0)
throw new Error(); // Can't happen
@ -558,6 +722,11 @@ class DatagramChannelImpl
sender = isa;
cachedSenderInetAddress = isa.getAddress();
cachedSenderPort = isa.getPort();
// Socket was not bound before connecting,
if (localAddress == null) {
localAddress = Net.localAddress(fd);
}
}
}
}
@ -584,9 +753,215 @@ class DatagramChannelImpl
return this;
}
/**
* Joins channel's socket to the given group/interface and
* optional source address.
*/
private MembershipKey innerJoin(InetAddress group,
NetworkInterface interf,
InetAddress source)
throws IOException
{
if (!group.isMulticastAddress())
throw new IllegalArgumentException("Group not a multicast address");
// check multicast address is compatible with this socket
if (!(group instanceof Inet4Address)) {
if (family == StandardProtocolFamily.INET)
throw new IllegalArgumentException("Group is not IPv4 address");
if (!(group instanceof Inet6Address))
throw new IllegalArgumentException("Address type not supported");
}
// check source address
if (source != null) {
if (source.isAnyLocalAddress())
throw new IllegalArgumentException("Source address is a wildcard address");
if (source.isMulticastAddress())
throw new IllegalArgumentException("Source address is multicast address");
if (source.getClass() != group.getClass())
throw new IllegalArgumentException("Source address is different type to group");
}
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkMulticast(group);
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
// check the registry to see if we are already a member of the group
if (registry == null) {
registry = new MembershipRegistry();
} else {
// return existing membership key
MembershipKey key = registry.checkMembership(group, interf, source);
if (key != null)
return key;
}
MembershipKeyImpl key;
if (family == StandardProtocolFamily.INET6) {
int index = interf.getIndex();
if (index == -1)
throw new IOException("Network interface cannot be identified");
// need multicast and source address as byte arrays
byte[] groupAddress = Net.inet6AsByteArray(group);
byte[] sourceAddress = (source == null) ? null :
Net.inet6AsByteArray(source);
// join the group
int n = Net.join6(fd, groupAddress, index, sourceAddress);
if (n == IOStatus.UNAVAILABLE)
throw new UnsupportedOperationException();
key = new MembershipKeyImpl.Type6(this, group, interf, source,
groupAddress, index, sourceAddress);
} else {
// need IPv4 address to identify interface
Inet4Address target = Net.anyInet4Address(interf);
if (target == null)
throw new IOException("Network interface not configured for IPv4");
int groupAddress = Net.inet4AsInt(group);
int targetAddress = Net.inet4AsInt(target);
int sourceAddress = (source == null) ? 0 : Net.inet4AsInt(source);
// join the group
int n = Net.join4(fd, groupAddress, targetAddress, sourceAddress);
if (n == IOStatus.UNAVAILABLE)
throw new UnsupportedOperationException();
key = new MembershipKeyImpl.Type4(this, group, interf, source,
groupAddress, targetAddress, sourceAddress);
}
registry.add(key);
return key;
}
}
@Override
public MembershipKey join(InetAddress group,
NetworkInterface interf)
throws IOException
{
return innerJoin(group, interf, null);
}
@Override
public MembershipKey join(InetAddress group,
NetworkInterface interf,
InetAddress source)
throws IOException
{
if (source == null)
throw new NullPointerException("source address is null");
return innerJoin(group, interf, source);
}
// package-private
void drop(MembershipKeyImpl key)
throws IOException
{
assert key.getChannel() == this;
synchronized (stateLock) {
if (!key.isValid())
return;
if (family == StandardProtocolFamily.INET6) {
MembershipKeyImpl.Type6 key6 =
(MembershipKeyImpl.Type6)key;
Net.drop6(fd, key6.group(), key6.index(), key6.source());
} else {
MembershipKeyImpl.Type4 key4 =
(MembershipKeyImpl.Type4)key;
Net.drop4(fd, key4.group(), key4.interfaceAddress(), key4.source());
}
key.invalidate();
registry.remove(key);
}
}
/**
* Block datagrams from given source if a memory to receive all
* datagrams.
*/
void block(MembershipKeyImpl key, InetAddress source)
throws IOException
{
assert key.getChannel() == this;
assert key.getSourceAddress() == null;
synchronized (stateLock) {
if (!key.isValid())
throw new IllegalStateException("key is no longer valid");
if (source.isAnyLocalAddress())
throw new IllegalArgumentException("Source address is a wildcard address");
if (source.isMulticastAddress())
throw new IllegalArgumentException("Source address is multicast address");
if (source.getClass() != key.getGroup().getClass())
throw new IllegalArgumentException("Source address is different type to group");
int n;
if (family == StandardProtocolFamily.INET6) {
MembershipKeyImpl.Type6 key6 =
(MembershipKeyImpl.Type6)key;
n = Net.block6(fd, key6.group(), key6.index(),
Net.inet6AsByteArray(source));
} else {
MembershipKeyImpl.Type4 key4 =
(MembershipKeyImpl.Type4)key;
n = Net.block4(fd, key4.group(), key4.interfaceAddress(),
Net.inet4AsInt(source));
}
if (n == IOStatus.UNAVAILABLE) {
// ancient kernel
throw new UnsupportedOperationException();
}
}
}
/**
* Unblock given source.
*/
void unblock(MembershipKeyImpl key, InetAddress source)
throws IOException
{
assert key.getChannel() == this;
assert key.getSourceAddress() == null;
synchronized (stateLock) {
if (!key.isValid())
throw new IllegalStateException("key is no longer valid");
if (family == StandardProtocolFamily.INET6) {
MembershipKeyImpl.Type6 key6 =
(MembershipKeyImpl.Type6)key;
Net.unblock6(fd, key6.group(), key6.index(),
Net.inet6AsByteArray(source));
} else {
MembershipKeyImpl.Type4 key4 =
(MembershipKeyImpl.Type4)key;
Net.unblock4(fd, key4.group(), key4.interfaceAddress(),
Net.inet4AsInt(source));
}
}
}
protected void implCloseSelectableChannel() throws IOException {
synchronized (stateLock) {
nd.preClose(fd);
// if member of mulitcast group then invalidate all keys
if (registry != null)
registry.invalidateAll();
long th;
if ((th = readerThread) != 0)
NativeThread.signal(th);
@ -695,7 +1070,7 @@ class DatagramChannelImpl
boolean connected)
throws IOException;
private native int send0(FileDescriptor fd, long address, int len,
private native int send0(boolean preferIPv6, FileDescriptor fd, long address, int len,
SocketAddress sa)
throws IOException;

View file

@ -45,16 +45,9 @@ public class DatagramSocketAdaptor
// The channel being adapted
private final DatagramChannelImpl dc;
// Option adaptor object, created on demand
private volatile OptionAdaptor opts = null;
// Timeout "option" value for receives
private volatile int timeout = 0;
// Traffic-class/Type-of-service
private volatile int trafficClass = 0;
// ## super will create a useless impl
private DatagramSocketAdaptor(DatagramChannelImpl dc) throws IOException {
// Invoke the DatagramSocketAdaptor(SocketAddress) constructor,
@ -82,7 +75,7 @@ public class DatagramSocketAdaptor
throw new IllegalArgumentException("connect: " + port);
if (remote == null)
throw new IllegalArgumentException("connect: null address");
if (!isClosed())
if (isClosed())
return;
try {
dc.connect(remote);
@ -124,11 +117,11 @@ public class DatagramSocketAdaptor
}
public boolean isBound() {
return dc.isBound();
return dc.localAddress() != null;
}
public boolean isConnected() {
return dc.isConnected();
return dc.remoteAddress() != null;
}
public InetAddress getInetAddress() {
@ -157,7 +150,7 @@ public class DatagramSocketAdaptor
// Legacy DatagramSocket will send in this case
// and set address and port of the packet
InetSocketAddress isa = (InetSocketAddress)
dc.remoteAddress;
dc.remoteAddress();
p.setPort(isa.getPort());
p.setAddress(isa.getAddress());
dc.write(bb);
@ -241,21 +234,32 @@ public class DatagramSocketAdaptor
public InetAddress getLocalAddress() {
if (isClosed())
return null;
SocketAddress local = dc.localAddress();
if (local == null)
local = new InetSocketAddress(0);
InetAddress result = ((InetSocketAddress)local).getAddress();
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
return Net.asInetSocketAddress(dc.localAddress()).getAddress();
} catch (Exception x) {
sm.checkConnect(result.getHostAddress(), -1);
} catch (SecurityException x) {
return new InetSocketAddress(0).getAddress();
}
}
return result;
}
public int getLocalPort() {
if (isClosed())
return -1;
try {
return Net.asInetSocketAddress(dc.localAddress()).getPort();
} catch (Exception x) {
return 0;
SocketAddress local = dc.getLocalAddress();
if (local != null) {
return ((InetSocketAddress)local).getPort();
}
} catch (Exception x) {
}
return 0;
}
public void setSoTimeout(int timeout) throws SocketException {
@ -266,55 +270,87 @@ public class DatagramSocketAdaptor
return timeout;
}
private OptionAdaptor opts() {
if (opts == null)
opts = new OptionAdaptor(dc);
return opts;
private void setBooleanOption(SocketOption<Boolean> name, boolean value)
throws SocketException
{
try {
dc.setOption(name, value);
} catch (IOException x) {
Net.translateToSocketException(x);
}
}
private void setIntOption(SocketOption<Integer> name, int value)
throws SocketException
{
try {
dc.setOption(name, value);
} catch (IOException x) {
Net.translateToSocketException(x);
}
}
private boolean getBooleanOption(SocketOption<Boolean> name) throws SocketException {
try {
return dc.getOption(name).booleanValue();
} catch (IOException x) {
Net.translateToSocketException(x);
return false; // keep compiler happy
}
}
private int getIntOption(SocketOption<Integer> name) throws SocketException {
try {
return dc.getOption(name).intValue();
} catch (IOException x) {
Net.translateToSocketException(x);
return -1; // keep compiler happy
}
}
public void setSendBufferSize(int size) throws SocketException {
opts().setSendBufferSize(size);
if (size <= 0)
throw new IllegalArgumentException("Invalid send size");
setIntOption(StandardSocketOption.SO_SNDBUF, size);
}
public int getSendBufferSize() throws SocketException {
return opts().getSendBufferSize();
return getIntOption(StandardSocketOption.SO_SNDBUF);
}
public void setReceiveBufferSize(int size) throws SocketException {
opts().setReceiveBufferSize(size);
if (size <= 0)
throw new IllegalArgumentException("Invalid receive size");
setIntOption(StandardSocketOption.SO_RCVBUF, size);
}
public int getReceiveBufferSize() throws SocketException {
return opts().getReceiveBufferSize();
return getIntOption(StandardSocketOption.SO_RCVBUF);
}
public void setReuseAddress(boolean on) throws SocketException {
opts().setReuseAddress(on);
setBooleanOption(StandardSocketOption.SO_REUSEADDR, on);
}
public boolean getReuseAddress() throws SocketException {
return opts().getReuseAddress();
return getBooleanOption(StandardSocketOption.SO_REUSEADDR);
}
public void setBroadcast(boolean on) throws SocketException {
opts().setBroadcast(on);
setBooleanOption(StandardSocketOption.SO_BROADCAST, on);
}
public boolean getBroadcast() throws SocketException {
return opts().getBroadcast();
return getBooleanOption(StandardSocketOption.SO_BROADCAST);
}
public void setTrafficClass(int tc) throws SocketException {
opts().setTrafficClass(tc);
trafficClass = tc;
setIntOption(StandardSocketOption.IP_TOS, tc);
}
public int getTrafficClass() throws SocketException {
int tc = opts().getTrafficClass();
if (tc < 0) {
tc = trafficClass;
}
return tc;
return getIntOption(StandardSocketOption.IP_TOS);
}
public void close() {

View file

@ -0,0 +1,44 @@
/*
* Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import java.net.SocketOption;
/**
* Defines socket options that are supported by the implementation
* but not defined in StandardSocketOption.
*/
class ExtendedSocketOption {
private ExtendedSocketOption() { }
static final SocketOption<Boolean> SO_OOBINLINE =
new SocketOption<Boolean>() {
public String name() { return "SO_OOBINLINE"; }
public Class<Boolean> type() { return Boolean.class; }
public String toString() { return name(); }
};
}

View file

@ -0,0 +1,222 @@
/*
* Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import java.nio.channels.*;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.io.IOException;
import java.util.HashSet;
/**
* MembershipKey implementation.
*/
class MembershipKeyImpl
extends MembershipKey
{
private final MulticastChannel ch;
private final InetAddress group;
private final NetworkInterface interf;
private final InetAddress source;
// true when key is valid
private volatile boolean valid = true;
// lock used when creating or accessing blockedSet
private Object stateLock = new Object();
// set of source addresses that are blocked
private HashSet<InetAddress> blockedSet;
private MembershipKeyImpl(MulticastChannel ch,
InetAddress group,
NetworkInterface interf,
InetAddress source)
{
this.ch = ch;
this.group = group;
this.interf = interf;
this.source = source;
}
/**
* MembershipKey will additional context for IPv4 membership
*/
static class Type4 extends MembershipKeyImpl {
private final int groupAddress;
private final int interfAddress;
private final int sourceAddress;
Type4(MulticastChannel ch,
InetAddress group,
NetworkInterface interf,
InetAddress source,
int groupAddress,
int interfAddress,
int sourceAddress)
{
super(ch, group, interf, source);
this.groupAddress = groupAddress;
this.interfAddress = interfAddress;
this.sourceAddress = sourceAddress;
}
int group() {
return groupAddress;
}
int interfaceAddress() {
return interfAddress;
}
int source() {
return sourceAddress;
}
}
/**
* MembershipKey will additional context for IPv6 membership
*/
static class Type6 extends MembershipKeyImpl {
private final byte[] groupAddress;
private final int index;
private final byte[] sourceAddress;
Type6(MulticastChannel ch,
InetAddress group,
NetworkInterface interf,
InetAddress source,
byte[] groupAddress,
int index,
byte[] sourceAddress)
{
super(ch, group, interf, source);
this.groupAddress = groupAddress;
this.index = index;
this.sourceAddress = sourceAddress;
}
byte[] group() {
return groupAddress;
}
int index() {
return index;
}
byte[] source() {
return sourceAddress;
}
}
public boolean isValid() {
return valid;
}
// package-private
void invalidate() {
valid = false;
}
public void drop() throws IOException {
// delegate to channel
((DatagramChannelImpl)ch).drop(this);
}
@Override
public MulticastChannel getChannel() {
return ch;
}
@Override
public InetAddress getGroup() {
return group;
}
@Override
public NetworkInterface getNetworkInterface() {
return interf;
}
@Override
public InetAddress getSourceAddress() {
return source;
}
@Override
public MembershipKey block(InetAddress toBlock)
throws IOException
{
if (source != null)
throw new IllegalStateException("key is source-specific");
synchronized (stateLock) {
if ((blockedSet != null) && blockedSet.contains(toBlock)) {
// already blocked, nothing to do
return this;
}
((DatagramChannelImpl)ch).block(this, toBlock);
// created blocked set if required and add source address
if (blockedSet == null)
blockedSet = new HashSet<InetAddress>();
blockedSet.add(toBlock);
}
return this;
}
@Override
public MembershipKey unblock(InetAddress toUnblock)
throws IOException
{
synchronized (stateLock) {
if ((blockedSet == null) || !blockedSet.contains(toUnblock))
throw new IllegalStateException("not blocked");
((DatagramChannelImpl)ch).unblock(this, toUnblock);
blockedSet.remove(toUnblock);
}
return this;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(64);
sb.append('<');
sb.append(group.getHostAddress());
sb.append(',');
sb.append(interf.getName());
if (source != null) {
sb.append(',');
sb.append(source.getHostAddress());
}
sb.append('>');
return sb.toString();
}
}

View file

@ -0,0 +1,129 @@
/*
* Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import java.nio.channels.*;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.*;
/**
* Simple registry of membership keys for a MulticastChannel.
*
* Instances of this object are not safe by multiple concurrent threads.
*/
class MembershipRegistry {
// map multicast group to keys
private Map<InetAddress,List<MembershipKeyImpl>> groups = null;
MembershipRegistry() {
}
/**
* Checks registry for membership of the group on the given
* network interface.
*/
MembershipKey checkMembership(InetAddress group, NetworkInterface interf,
InetAddress source)
{
if (groups != null) {
List<MembershipKeyImpl> keys = groups.get(group);
if (keys != null) {
for (MembershipKeyImpl key: keys) {
if (key.getNetworkInterface().equals(interf)) {
// already a member to receive all packets so return
// existing key or detect conflict
if (source == null) {
if (key.getSourceAddress() == null)
return key;
throw new IllegalStateException("Already a member to receive all packets");
}
// already have source-specific membership so return key
// or detect conflict
if (key.getSourceAddress() == null)
throw new IllegalStateException("Already have source-specific membership");
if (source.equals(key.getSourceAddress()))
return key;
}
}
}
}
return null;
}
/**
* Add membership to the registry, returning a new membership key.
*/
void add(MembershipKeyImpl key) {
InetAddress group = key.getGroup();
List<MembershipKeyImpl> keys;
if (groups == null) {
groups = new HashMap<InetAddress,List<MembershipKeyImpl>>();
keys = null;
} else {
keys = groups.get(group);
}
if (keys == null) {
keys = new LinkedList<MembershipKeyImpl>();
groups.put(group, keys);
}
keys.add(key);
}
/**
* Remove a key from the registry
*/
void remove(MembershipKeyImpl key) {
InetAddress group = key.getGroup();
List<MembershipKeyImpl> keys = groups.get(group);
if (keys != null) {
Iterator<MembershipKeyImpl> i = keys.iterator();
while (i.hasNext()) {
if (i.next() == key) {
i.remove();
break;
}
}
if (keys.isEmpty()) {
groups.remove(group);
}
}
}
/**
* Invalidate all keys in the registry
*/
void invalidateAll() {
for (InetAddress group: groups.keySet()) {
for (MembershipKeyImpl key: groups.get(group)) {
key.invalidate();
}
}
}
}

View file

@ -26,21 +26,43 @@
package sun.nio.ch;
import java.io.*;
import java.lang.reflect.*;
import java.net.*;
import java.nio.channels.*;
import java.util.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
class Net { // package-private
private Net() { }
// unspecified protocol family
static final ProtocolFamily UNSPEC = new ProtocolFamily() {
public String name() {
return "UNSPEC";
}
};
// -- Miscellaneous utilities --
private static volatile boolean checkedIPv6 = false;
private static volatile boolean isIPv6Available;
/**
* Tells whether dual-IPv4/IPv6 sockets should be used.
*/
static boolean isIPv6Available() {
if (!checkedIPv6) {
isIPv6Available = isIPv6Available0();
checkedIPv6 = true;
}
return isIPv6Available;
}
static InetSocketAddress checkAddress(SocketAddress sa) {
if (sa == null)
throw new IllegalArgumentException();
throw new NullPointerException();
if (!(sa instanceof InetSocketAddress))
throw new UnsupportedAddressTypeException(); // ## needs arg
InetSocketAddress isa = (InetSocketAddress)sa;
@ -63,6 +85,8 @@ class Net { // package-private
Exception nx = x;
if (x instanceof ClosedChannelException)
nx = new SocketException("Socket is closed");
else if (x instanceof NotYetConnectedException)
nx = new SocketException("Socket is not connected");
else if (x instanceof AlreadyBoundException)
nx = new SocketException("Already bound");
else if (x instanceof NotYetBoundException)
@ -105,73 +129,359 @@ class Net { // package-private
translateException(x, false);
}
/**
* Returns any IPv4 address of the given network interface, or
* null if the interface does not have any IPv4 addresses.
*/
static Inet4Address anyInet4Address(final NetworkInterface interf) {
return AccessController.doPrivileged(new PrivilegedAction<Inet4Address>() {
public Inet4Address run() {
Enumeration<InetAddress> addrs = interf.getInetAddresses();
while (addrs.hasMoreElements()) {
InetAddress addr = addrs.nextElement();
if (addr instanceof Inet4Address) {
return (Inet4Address)addr;
}
}
return null;
}
});
}
/**
* Returns an IPv4 address as an int.
*/
static int inet4AsInt(InetAddress ia) {
if (ia instanceof Inet4Address) {
byte[] addr = ia.getAddress();
int address = addr[3] & 0xFF;
address |= ((addr[2] << 8) & 0xFF00);
address |= ((addr[1] << 16) & 0xFF0000);
address |= ((addr[0] << 24) & 0xFF000000);
return address;
}
throw new AssertionError("Should not reach here");
}
/**
* Returns an InetAddress from the given IPv4 address
* represented as an int.
*/
static InetAddress inet4FromInt(int address) {
byte[] addr = new byte[4];
addr[0] = (byte) ((address >>> 24) & 0xFF);
addr[1] = (byte) ((address >>> 16) & 0xFF);
addr[2] = (byte) ((address >>> 8) & 0xFF);
addr[3] = (byte) (address & 0xFF);
try {
return InetAddress.getByAddress(addr);
} catch (UnknownHostException uhe) {
throw new AssertionError("Should not reach here");
}
}
/**
* Returns an IPv6 address as a byte array
*/
static byte[] inet6AsByteArray(InetAddress ia) {
if (ia instanceof Inet6Address) {
return ia.getAddress();
}
// need to construct IPv4-mapped address
if (ia instanceof Inet4Address) {
byte[] ip4address = ia.getAddress();
byte[] address = new byte[16];
address[10] = (byte)0xff;
address[11] = (byte)0xff;
address[12] = ip4address[0];
address[13] = ip4address[1];
address[14] = ip4address[2];
address[15] = ip4address[3];
return address;
}
throw new AssertionError("Should not reach here");
}
// -- Socket options
static void setSocketOption(FileDescriptor fd, ProtocolFamily family,
SocketOption name, Object value)
throws IOException
{
if (value == null)
throw new IllegalArgumentException("Invalid option value");
// only simple values supported by this method
Class<?> type = name.type();
if (type != Integer.class && type != Boolean.class)
throw new AssertionError("Should not reach here");
// special handling
if (name == StandardSocketOption.SO_RCVBUF ||
name == StandardSocketOption.SO_SNDBUF)
{
int i = ((Integer)value).intValue();
if (i < 0)
throw new IllegalArgumentException("Invalid send/receive buffer size");
}
if (name == StandardSocketOption.SO_LINGER) {
int i = ((Integer)value).intValue();
if (i < 0)
value = Integer.valueOf(-1);
if (i > 65535)
value = Integer.valueOf(65535);
}
if (name == StandardSocketOption.IP_TOS) {
int i = ((Integer)value).intValue();
if (i < 0 || i > 255)
throw new IllegalArgumentException("Invalid IP_TOS value");
}
if (name == StandardSocketOption.IP_MULTICAST_TTL) {
int i = ((Integer)value).intValue();
if (i < 0 || i > 255)
throw new IllegalArgumentException("Invalid TTL/hop value");
}
// map option name to platform level/name
OptionKey key = SocketOptionRegistry.findOption(name, family);
if (key == null)
throw new AssertionError("Option not found");
int arg;
if (type == Integer.class) {
arg = ((Integer)value).intValue();
} else {
boolean b = ((Boolean)value).booleanValue();
arg = (b) ? 1 : 0;
}
boolean mayNeedConversion = (family == UNSPEC);
setIntOption0(fd, mayNeedConversion, key.level(), key.name(), arg);
}
static Object getSocketOption(FileDescriptor fd, ProtocolFamily family,
SocketOption name)
throws IOException
{
Class<?> type = name.type();
// only simple values supported by this method
if (type != Integer.class && type != Boolean.class)
throw new AssertionError("Should not reach here");
// map option name to platform level/name
OptionKey key = SocketOptionRegistry.findOption(name, family);
if (key == null)
throw new AssertionError("Option not found");
boolean mayNeedConversion = (family == UNSPEC);
int value = getIntOption0(fd, mayNeedConversion, key.level(), key.name());
if (type == Integer.class) {
return Integer.valueOf(value);
} else {
return (value == 0) ? Boolean.FALSE : Boolean.TRUE;
}
}
// -- Socket operations --
static native boolean isIPv6Available0();
static FileDescriptor socket(boolean stream) {
return IOUtil.newFD(socket0(stream, false));
return socket(UNSPEC, stream);
}
static FileDescriptor socket(ProtocolFamily family, boolean stream) {
boolean preferIPv6 = isIPv6Available() &&
(family != StandardProtocolFamily.INET);
return IOUtil.newFD(socket0(preferIPv6, stream, false));
}
static FileDescriptor serverSocket(boolean stream) {
return IOUtil.newFD(socket0(stream, true));
return IOUtil.newFD(socket0(isIPv6Available(), stream, true));
}
// Due to oddities SO_REUSEADDR on windows reuse is ignored
private static native int socket0(boolean stream, boolean reuse);
private static native int socket0(boolean preferIPv6, boolean stream, boolean reuse);
static native void bind(FileDescriptor fd, InetAddress addr, int port)
static void bind(FileDescriptor fd, InetAddress addr, int port)
throws IOException
{
bind(UNSPEC, fd, addr, port);
}
static void bind(ProtocolFamily family, FileDescriptor fd,
InetAddress addr, int port) throws IOException
{
boolean preferIPv6 = isIPv6Available() &&
(family != StandardProtocolFamily.INET);
bind0(preferIPv6, fd, addr, port);
}
private static native void bind0(boolean preferIPv6, FileDescriptor fd,
InetAddress addr, int port)
throws IOException;
static native int connect(FileDescriptor fd,
static native void listen(FileDescriptor fd, int backlog) throws IOException;
static int connect(FileDescriptor fd, InetAddress remote, int remotePort)
throws IOException
{
return connect(UNSPEC, fd, remote, remotePort);
}
static int connect(ProtocolFamily family, FileDescriptor fd, InetAddress remote, int remotePort)
throws IOException
{
boolean preferIPv6 = isIPv6Available() &&
(family != StandardProtocolFamily.INET);
return connect0(preferIPv6, fd, remote, remotePort);
}
private static native int connect0(boolean preferIPv6,
FileDescriptor fd,
InetAddress remote,
int remotePort,
int trafficClass)
int remotePort)
throws IOException;
public final static int SHUT_RD = 0;
public final static int SHUT_WR = 1;
public final static int SHUT_RDWR = 2;
static native void shutdown(FileDescriptor fd, int how) throws IOException;
private static native int localPort(FileDescriptor fd)
throws IOException;
private static native InetAddress localInetAddress(FileDescriptor fd)
throws IOException;
static InetSocketAddress localAddress(FileDescriptor fd) {
try {
return new InetSocketAddress(localInetAddress(fd),
localPort(fd));
} catch (IOException x) {
throw new Error(x); // Can't happen
}
}
static int localPortNumber(FileDescriptor fd) {
try {
return localPort(fd);
} catch (IOException x) {
throw new Error(x); // Can't happen
}
}
private static native int getIntOption0(FileDescriptor fd, int opt)
throws IOException;
static int getIntOption(FileDescriptor fd, int opt)
static InetSocketAddress localAddress(FileDescriptor fd)
throws IOException
{
return getIntOption0(fd, opt);
return new InetSocketAddress(localInetAddress(fd), localPort(fd));
}
private static native void setIntOption0(FileDescriptor fd,
int opt, int arg)
private static native int remotePort(FileDescriptor fd)
throws IOException;
static void setIntOption(FileDescriptor fd, int opt, int arg)
private static native InetAddress remoteInetAddress(FileDescriptor fd)
throws IOException;
static InetSocketAddress remoteAddress(FileDescriptor fd)
throws IOException
{
setIntOption0(fd, opt, arg);
return new InetSocketAddress(remoteInetAddress(fd), remotePort(fd));
}
private static native int getIntOption0(FileDescriptor fd, boolean mayNeedConversion,
int level, int opt)
throws IOException;
private static native void setIntOption0(FileDescriptor fd, boolean mayNeedConversion,
int level, int opt, int arg)
throws IOException;
// -- Multicast support --
/**
* Join IPv4 multicast group
*/
static int join4(FileDescriptor fd, int group, int interf, int source)
throws IOException
{
return joinOrDrop4(true, fd, group, interf, source);
}
/**
* Drop membership of IPv4 multicast group
*/
static void drop4(FileDescriptor fd, int group, int interf, int source)
throws IOException
{
joinOrDrop4(false, fd, group, interf, source);
}
private static native int joinOrDrop4(boolean join, FileDescriptor fd, int group, int interf, int source)
throws IOException;
/**
* Block IPv4 source
*/
static int block4(FileDescriptor fd, int group, int interf, int source)
throws IOException
{
return blockOrUnblock4(true, fd, group, interf, source);
}
/**
* Unblock IPv6 source
*/
static void unblock4(FileDescriptor fd, int group, int interf, int source)
throws IOException
{
blockOrUnblock4(false, fd, group, interf, source);
}
private static native int blockOrUnblock4(boolean block, FileDescriptor fd, int group,
int interf, int source)
throws IOException;
/**
* Join IPv6 multicast group
*/
static int join6(FileDescriptor fd, byte[] group, int index, byte[] source)
throws IOException
{
return joinOrDrop6(true, fd, group, index, source);
}
/**
* Drop membership of IPv6 multicast group
*/
static void drop6(FileDescriptor fd, byte[] group, int index, byte[] source)
throws IOException
{
joinOrDrop6(false, fd, group, index, source);
}
private static native int joinOrDrop6(boolean join, FileDescriptor fd, byte[] group, int index, byte[] source)
throws IOException;
/**
* Block IPv6 source
*/
static int block6(FileDescriptor fd, byte[] group, int index, byte[] source)
throws IOException
{
return blockOrUnblock6(true, fd, group, index, source);
}
/**
* Unblock IPv6 source
*/
static void unblock6(FileDescriptor fd, byte[] group, int index, byte[] source)
throws IOException
{
blockOrUnblock6(false, fd, group, index, source);
}
static native int blockOrUnblock6(boolean block, FileDescriptor fd, byte[] group, int index, byte[] source)
throws IOException;
static native void setInterface4(FileDescriptor fd, int interf) throws IOException;
static native int getInterface4(FileDescriptor fd) throws IOException;
static native void setInterface6(FileDescriptor fd, int index) throws IOException;
static native int getInterface6(FileDescriptor fd) throws IOException;
private static native void initIDs();
static {

View file

@ -1,229 +0,0 @@
/*
* Copyright 2001 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
// Adaptor class for java.net-style options
//
// The option get/set methods in the socket, server-socket, and datagram-socket
// adaptors delegate to an instance of this class.
//
class OptionAdaptor { // package-private
private final SocketOpts.IP opts;
OptionAdaptor(SocketChannelImpl sc) {
opts = (SocketOpts.IP)sc.options();
}
OptionAdaptor(ServerSocketChannelImpl ssc) {
opts = (SocketOpts.IP)ssc.options();
}
OptionAdaptor(DatagramChannelImpl dc) {
opts = (SocketOpts.IP)dc.options();
}
private SocketOpts.IP opts() {
return opts;
}
private SocketOpts.IP.TCP tcpOpts() {
return (SocketOpts.IP.TCP)opts;
}
public void setTcpNoDelay(boolean on) throws SocketException {
try {
tcpOpts().noDelay(on);
} catch (Exception x) {
Net.translateToSocketException(x);
}
}
public boolean getTcpNoDelay() throws SocketException {
try {
return tcpOpts().noDelay();
} catch (Exception x) {
Net.translateToSocketException(x);
return false; // Never happens
}
}
public void setSoLinger(boolean on, int linger) throws SocketException {
try {
if (linger > 65535)
linger = 65535;
opts().linger(on ? linger : -1);
} catch (Exception x) {
Net.translateToSocketException(x);
}
}
public int getSoLinger() throws SocketException {
try {
return opts().linger();
} catch (Exception x) {
Net.translateToSocketException(x);
return 0; // Never happens
}
}
public void setOOBInline(boolean on) throws SocketException {
try {
opts().outOfBandInline(on);
} catch (Exception x) {
Net.translateToSocketException(x);
}
}
public boolean getOOBInline() throws SocketException {
try {
return opts().outOfBandInline();
} catch (Exception x) {
Net.translateToSocketException(x);
return false; // Never happens
}
}
public void setSendBufferSize(int size)
throws SocketException
{
try {
opts().sendBufferSize(size);
} catch (Exception x) {
Net.translateToSocketException(x);
}
}
public int getSendBufferSize() throws SocketException {
try {
return opts().sendBufferSize();
} catch (Exception x) {
Net.translateToSocketException(x);
return 0; // Never happens
}
}
public void setReceiveBufferSize(int size)
throws SocketException
{
try {
opts().receiveBufferSize(size);
} catch (Exception x) {
Net.translateToSocketException(x);
}
}
public int getReceiveBufferSize() throws SocketException {
try {
return opts().receiveBufferSize();
} catch (Exception x) {
Net.translateToSocketException(x);
return 0; // Never happens
}
}
public void setKeepAlive(boolean on) throws SocketException {
try {
opts().keepAlive(on);
} catch (Exception x) {
Net.translateToSocketException(x);
}
}
public boolean getKeepAlive() throws SocketException {
try {
return opts().keepAlive();
} catch (Exception x) {
Net.translateToSocketException(x);
return false; // Never happens
}
}
public void setTrafficClass(int tc) throws SocketException {
if (tc < 0 || tc > 255)
throw new IllegalArgumentException("tc is not in range 0 -- 255");
try {
opts().typeOfService(tc);
} catch (Exception x) {
Net.translateToSocketException(x);
}
}
public int getTrafficClass() throws SocketException {
try {
return opts().typeOfService();
} catch (Exception x) {
Net.translateToSocketException(x);
return 0; // Never happens
}
}
public void setReuseAddress(boolean on)
throws SocketException
{
try {
opts().reuseAddress(on);
} catch (Exception x) {
Net.translateToSocketException(x);
}
}
public boolean getReuseAddress() throws SocketException {
try {
return opts().reuseAddress();
} catch (Exception x) {
Net.translateToSocketException(x);
return false; // Never happens
}
}
public void setBroadcast(boolean on)
throws SocketException
{
try {
opts().broadcast(on);
} catch (Exception x) {
Net.translateToSocketException(x);
}
}
public boolean getBroadcast() throws SocketException {
try {
return opts().broadcast();
} catch (Exception x) {
Net.translateToSocketException(x);
return false; // Never happens
}
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
/**
* Represents the level/name of a socket option
*/
class OptionKey {
private int level;
private int name;
OptionKey(int level, int name) {
this.level = level;
this.name = name;
}
int level() {
return level;
}
int name() {
return name;
}
}

View file

@ -29,6 +29,7 @@ import java.io.FileDescriptor;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.ProtocolFamily;
import java.nio.channels.*;
import java.nio.channels.spi.*;
@ -41,6 +42,10 @@ public abstract class SelectorProviderImpl
return new DatagramChannelImpl(this);
}
public DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException {
return new DatagramChannelImpl(this, family);
}
public Pipe openPipe() throws IOException {
return new PipeImpl(this);
}
@ -54,5 +59,4 @@ public abstract class SelectorProviderImpl
public SocketChannel openSocketChannel() throws IOException {
return new SocketChannelImpl(this);
}
}

View file

@ -44,9 +44,6 @@ public class ServerSocketAdaptor // package-private
// The channel being adapted
private final ServerSocketChannelImpl ssc;
// Option adaptor object, created on demand
private volatile OptionAdaptor opts = null;
// Timeout "option" value for accepts
private volatile int timeout = 0;
@ -174,18 +171,21 @@ public class ServerSocketAdaptor // package-private
return timeout;
}
private OptionAdaptor opts() {
if (opts == null)
opts = new OptionAdaptor(ssc);
return opts;
}
public void setReuseAddress(boolean on) throws SocketException {
opts().setReuseAddress(on);
try {
ssc.setOption(StandardSocketOption.SO_REUSEADDR, on);
} catch (IOException x) {
Net.translateToSocketException(x);
}
}
public boolean getReuseAddress() throws SocketException {
return opts().getReuseAddress();
try {
return ssc.getOption(StandardSocketOption.SO_REUSEADDR).booleanValue();
} catch (IOException x) {
Net.translateToSocketException(x);
return false; // Never happens
}
}
public String toString() {
@ -197,11 +197,23 @@ public class ServerSocketAdaptor // package-private
}
public void setReceiveBufferSize(int size) throws SocketException {
opts().setReceiveBufferSize(size);
// size 0 valid for ServerSocketChannel, invalid for ServerSocket
if (size <= 0)
throw new IllegalArgumentException("size cannot be 0 or negative");
try {
ssc.setOption(StandardSocketOption.SO_RCVBUF, size);
} catch (IOException x) {
Net.translateToSocketException(x);
}
}
public int getReceiveBufferSize() throws SocketException {
return opts().getReceiveBufferSize();
try {
return ssc.getOption(StandardSocketOption.SO_RCVBUF).intValue();
} catch (IOException x) {
Net.translateToSocketException(x);
return -1; // Never happens
}
}
}

View file

@ -33,8 +33,7 @@ import java.nio.channels.*;
import java.nio.channels.spi.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashSet;
import java.util.Iterator;
import java.util.*;
/**
@ -75,10 +74,7 @@ class ServerSocketChannelImpl
private int state = ST_UNINITIALIZED;
// Binding
private SocketAddress localAddress = null; // null => unbound
// Options, created on demand
private SocketOpts.IP.TCP options = null;
private SocketAddress localAddress; // null => unbound
// Our socket adaptor, if any
ServerSocket socket;
@ -103,7 +99,6 @@ class ServerSocketChannelImpl
localAddress = Net.localAddress(fd);
}
public ServerSocket socket() {
synchronized (stateLock) {
if (socket == null)
@ -112,6 +107,69 @@ class ServerSocketChannelImpl
}
}
@Override
public SocketAddress getLocalAddress() throws IOException {
synchronized (stateLock) {
if (!isOpen())
return null;
return localAddress;
}
}
@Override
public ServerSocketChannel setOption(SocketOption name, Object value)
throws IOException
{
if (name == null)
throw new NullPointerException();
if (!options().contains(name))
throw new IllegalArgumentException("invalid option name");
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
// no options that require special handling
Net.setSocketOption(fd, Net.UNSPEC, name, value);
return this;
}
}
@Override
@SuppressWarnings("unchecked")
public <T> T getOption(SocketOption<T> name)
throws IOException
{
if (name == null)
throw new NullPointerException();
if (!options().contains(name))
throw new IllegalArgumentException("invalid option name");
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
// no options that require special handling
return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
}
}
private static class LazyInitialization {
static final Set<SocketOption<?>> defaultOptions = defaultOptions();
private static Set<SocketOption<?>> defaultOptions() {
HashSet<SocketOption<?>> set = new HashSet<SocketOption<?>>(2);
set.add(StandardSocketOption.SO_RCVBUF);
set.add(StandardSocketOption.SO_REUSEADDR);
return Collections.unmodifiableSet(set);
}
}
@Override
public final Set<SocketOption<?>> options() {
return LazyInitialization.defaultOptions;
}
public boolean isBound() {
synchronized (stateLock) {
return localAddress != null;
@ -124,22 +182,25 @@ class ServerSocketChannelImpl
}
}
public void bind(SocketAddress local, int backlog) throws IOException {
@Override
public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException {
synchronized (lock) {
if (!isOpen())
throw new ClosedChannelException();
if (isBound())
throw new AlreadyBoundException();
InetSocketAddress isa = Net.checkAddress(local);
InetSocketAddress isa = (local == null) ? new InetSocketAddress(0) :
Net.checkAddress(local);
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkListen(isa.getPort());
Net.bind(fd, isa.getAddress(), isa.getPort());
listen(fd, backlog < 1 ? 50 : backlog);
Net.listen(fd, backlog < 1 ? 50 : backlog);
synchronized (stateLock) {
localAddress = Net.localAddress(fd);
}
}
return this;
}
public SocketChannel accept() throws IOException {
@ -196,24 +257,6 @@ class ServerSocketChannelImpl
IOUtil.configureBlocking(fd, block);
}
public SocketOpts options() {
synchronized (stateLock) {
if (options == null) {
SocketOptsImpl.Dispatcher d
= new SocketOptsImpl.Dispatcher() {
int getInt(int opt) throws IOException {
return Net.getIntOption(fd, opt);
}
void setInt(int opt, int arg) throws IOException {
Net.setIntOption(fd, opt, arg);
}
};
options = new SocketOptsImpl.IP.TCP(d);
}
return options;
}
}
protected void implCloseSelectableChannel() throws IOException {
synchronized (stateLock) {
nd.preClose(fd);
@ -320,9 +363,6 @@ class ServerSocketChannelImpl
// -- Native methods --
private static native void listen(FileDescriptor fd, int backlog)
throws IOException;
// Accepts a new connection, setting the given file descriptor to refer to
// the new socket and setting isaa[0] to the socket's remote address.
// Returns 1 on success, or IOStatus.UNAVAILABLE (if non-blocking and no

View file

@ -54,16 +54,9 @@ public class SocketAdaptor
// The channel being adapted
private final SocketChannelImpl sc;
// Option adaptor object, created on demand
private volatile OptionAdaptor opts = null;
// Timeout "option" value for reads
private volatile int timeout = 0;
// Traffic-class/Type-of-service
private volatile int trafficClass = 0;
// ## super will create a useless impl
private SocketAdaptor(SocketChannelImpl sc) {
this.sc = sc;
@ -145,8 +138,6 @@ public class SocketAdaptor
public void bind(SocketAddress local) throws IOException {
try {
if (local == null)
local = new InetSocketAddress(0);
sc.bind(local);
} catch (Exception x) {
Net.translateException(x);
@ -154,27 +145,39 @@ public class SocketAdaptor
}
public InetAddress getInetAddress() {
if (!sc.isConnected())
SocketAddress remote = sc.remoteAddress();
if (remote == null) {
return null;
return Net.asInetSocketAddress(sc.remoteAddress()).getAddress();
} else {
return ((InetSocketAddress)remote).getAddress();
}
}
public InetAddress getLocalAddress() {
if (!sc.isBound())
if (sc.isOpen()) {
SocketAddress local = sc.localAddress();
if (local != null)
return ((InetSocketAddress)local).getAddress();
}
return new InetSocketAddress(0).getAddress();
return Net.asInetSocketAddress(sc.localAddress()).getAddress();
}
public int getPort() {
if (!sc.isConnected())
SocketAddress remote = sc.remoteAddress();
if (remote == null) {
return 0;
return Net.asInetSocketAddress(sc.remoteAddress()).getPort();
} else {
return ((InetSocketAddress)remote).getPort();
}
}
public int getLocalPort() {
if (!sc.isBound())
SocketAddress local = sc.localAddress();
if (local == null) {
return -1;
return Net.asInetSocketAddress(sc.localAddress()).getPort();
} else {
return ((InetSocketAddress)local).getPort();
}
}
private class SocketInputStream
@ -276,26 +279,60 @@ public class SocketAdaptor
return os;
}
private OptionAdaptor opts() {
if (opts == null)
opts = new OptionAdaptor(sc);
return opts;
private void setBooleanOption(SocketOption<Boolean> name, boolean value)
throws SocketException
{
try {
sc.setOption(name, value);
} catch (IOException x) {
Net.translateToSocketException(x);
}
}
private void setIntOption(SocketOption<Integer> name, int value)
throws SocketException
{
try {
sc.setOption(name, value);
} catch (IOException x) {
Net.translateToSocketException(x);
}
}
private boolean getBooleanOption(SocketOption<Boolean> name) throws SocketException {
try {
return sc.getOption(name).booleanValue();
} catch (IOException x) {
Net.translateToSocketException(x);
return false; // keep compiler happy
}
}
private int getIntOption(SocketOption<Integer> name) throws SocketException {
try {
return sc.getOption(name).intValue();
} catch (IOException x) {
Net.translateToSocketException(x);
return -1; // keep compiler happy
}
}
public void setTcpNoDelay(boolean on) throws SocketException {
opts().setTcpNoDelay(on);
setBooleanOption(StandardSocketOption.TCP_NODELAY, on);
}
public boolean getTcpNoDelay() throws SocketException {
return opts().getTcpNoDelay();
return getBooleanOption(StandardSocketOption.TCP_NODELAY);
}
public void setSoLinger(boolean on, int linger) throws SocketException {
opts().setSoLinger(on, linger);
if (!on)
linger = -1;
setIntOption(StandardSocketOption.SO_LINGER, linger);
}
public int getSoLinger() throws SocketException {
return opts().getSoLinger();
return getIntOption(StandardSocketOption.SO_LINGER);
}
public void sendUrgentData(int data) throws IOException {
@ -303,11 +340,11 @@ public class SocketAdaptor
}
public void setOOBInline(boolean on) throws SocketException {
opts().setOOBInline(on);
setBooleanOption(ExtendedSocketOption.SO_OOBINLINE, on);
}
public boolean getOOBInline() throws SocketException {
return opts().getOOBInline();
return getBooleanOption(ExtendedSocketOption.SO_OOBINLINE);
}
public void setSoTimeout(int timeout) throws SocketException {
@ -321,48 +358,49 @@ public class SocketAdaptor
}
public void setSendBufferSize(int size) throws SocketException {
opts().setSendBufferSize(size);
// size 0 valid for SocketChannel, invalid for Socket
if (size <= 0)
throw new IllegalArgumentException("Invalid send size");
setIntOption(StandardSocketOption.SO_SNDBUF, size);
}
public int getSendBufferSize() throws SocketException {
return opts().getSendBufferSize();
return getIntOption(StandardSocketOption.SO_SNDBUF);
}
public void setReceiveBufferSize(int size) throws SocketException {
opts().setReceiveBufferSize(size);
// size 0 valid for SocketChannel, invalid for Socket
if (size <= 0)
throw new IllegalArgumentException("Invalid receive size");
setIntOption(StandardSocketOption.SO_RCVBUF, size);
}
public int getReceiveBufferSize() throws SocketException {
return opts().getReceiveBufferSize();
return getIntOption(StandardSocketOption.SO_RCVBUF);
}
public void setKeepAlive(boolean on) throws SocketException {
opts().setKeepAlive(on);
setBooleanOption(StandardSocketOption.SO_KEEPALIVE, on);
}
public boolean getKeepAlive() throws SocketException {
return opts().getKeepAlive();
return getBooleanOption(StandardSocketOption.SO_KEEPALIVE);
}
public void setTrafficClass(int tc) throws SocketException {
opts().setTrafficClass(tc);
trafficClass = tc;
setIntOption(StandardSocketOption.IP_TOS, tc);
}
public int getTrafficClass() throws SocketException {
int tc = opts().getTrafficClass();
if (tc < 0) {
tc = trafficClass;
}
return tc;
return getIntOption(StandardSocketOption.IP_TOS);
}
public void setReuseAddress(boolean on) throws SocketException {
opts().setReuseAddress(on);
setBooleanOption(StandardSocketOption.SO_REUSEADDR, on);
}
public boolean getReuseAddress() throws SocketException {
return opts().getReuseAddress();
return getBooleanOption(StandardSocketOption.SO_REUSEADDR);
}
public void close() throws IOException {
@ -402,7 +440,7 @@ public class SocketAdaptor
}
public boolean isBound() {
return sc.isBound();
return sc.localAddress() != null;
}
public boolean isClosed() {

View file

@ -31,6 +31,7 @@ import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.channels.spi.*;
import java.util.*;
/**
@ -78,19 +79,16 @@ class SocketChannelImpl
private int state = ST_UNINITIALIZED;
// Binding
private SocketAddress localAddress = null;
private SocketAddress remoteAddress = null;
private SocketAddress localAddress;
private SocketAddress remoteAddress;
// Input/Output open
private boolean isInputOpen = true;
private boolean isOutputOpen = true;
private boolean readyToConnect = false;
// Options, created on demand
private SocketOpts.IP.TCP options = null;
// Socket adaptor, created on demand
private Socket socket = null;
private Socket socket;
// -- End of fields protected by stateLock
@ -114,6 +112,7 @@ class SocketChannelImpl
this.fd = fd;
this.fdVal = IOUtil.fdVal(fd);
this.state = ST_CONNECTED;
this.localAddress = Net.localAddress(fd);
this.remoteAddress = remote;
}
@ -125,6 +124,98 @@ class SocketChannelImpl
}
}
@Override
public SocketAddress getLocalAddress() throws IOException {
synchronized (stateLock) {
if (!isOpen())
return null;
return localAddress;
}
}
@Override
public SocketAddress getConnectedAddress() throws IOException {
synchronized (stateLock) {
if (!isOpen())
return null;
return remoteAddress;
}
}
@Override
public SocketChannel setOption(SocketOption name, Object value)
throws IOException
{
if (name == null)
throw new NullPointerException();
if (!options().contains(name))
throw new IllegalArgumentException("Invalid option name");
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
// special handling for IP_TOS: no-op when IPv6
if (name == StandardSocketOption.IP_TOS) {
if (!Net.isIPv6Available())
Net.setSocketOption(fd, StandardProtocolFamily.INET, name, value);
return this;
}
// no options that require special handling
Net.setSocketOption(fd, Net.UNSPEC, name, value);
return this;
}
}
@Override
@SuppressWarnings("unchecked")
public <T> T getOption(SocketOption<T> name)
throws IOException
{
if (name == null)
throw new NullPointerException();
if (!options().contains(name))
throw new IllegalArgumentException("Invalid option name");
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
// special handling for IP_TOS: always return 0 when IPv6
if (name == StandardSocketOption.IP_TOS) {
return (Net.isIPv6Available()) ? (T) Integer.valueOf(0) :
(T) Net.getSocketOption(fd, StandardProtocolFamily.INET, name);
}
// no options that require special handling
return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
}
}
private static class LazyInitialization {
static final Set<SocketOption<?>> defaultOptions = defaultOptions();
private static Set<SocketOption<?>> defaultOptions() {
HashSet<SocketOption<?>> set = new HashSet<SocketOption<?>>(8);
set.add(StandardSocketOption.SO_SNDBUF);
set.add(StandardSocketOption.SO_RCVBUF);
set.add(StandardSocketOption.SO_KEEPALIVE);
set.add(StandardSocketOption.SO_REUSEADDR);
set.add(StandardSocketOption.SO_LINGER);
set.add(StandardSocketOption.TCP_NODELAY);
// additional options required by socket adaptor
set.add(StandardSocketOption.IP_TOS);
set.add(ExtendedSocketOption.SO_OOBINLINE);
return Collections.unmodifiableSet(set);
}
}
@Override
public final Set<SocketOption<?>> options() {
return LazyInitialization.defaultOptions;
}
private boolean ensureReadOpen() throws ClosedChannelException {
synchronized (stateLock) {
if (!isOpen())
@ -410,43 +501,8 @@ class SocketChannelImpl
IOUtil.configureBlocking(fd, block);
}
public SocketOpts options() {
synchronized (stateLock) {
if (options == null) {
SocketOptsImpl.Dispatcher d
= new SocketOptsImpl.Dispatcher() {
int getInt(int opt) throws IOException {
return Net.getIntOption(fd, opt);
}
void setInt(int opt, int arg)
throws IOException
{
Net.setIntOption(fd, opt, arg);
}
};
options = new SocketOptsImpl.IP.TCP(d);
}
return options;
}
}
public boolean isBound() {
synchronized (stateLock) {
if (state == ST_CONNECTED)
return true;
return localAddress != null;
}
}
public SocketAddress localAddress() {
synchronized (stateLock) {
if (state == ST_CONNECTED &&
(localAddress == null ||
((InetSocketAddress)localAddress).getAddress().isAnyLocalAddress())) {
// Socket was not bound before connecting or
// Socket was bound with an "anyLocalAddress"
localAddress = Net.localAddress(fd);
}
return localAddress;
}
}
@ -457,19 +513,25 @@ class SocketChannelImpl
}
}
public void bind(SocketAddress local) throws IOException {
@Override
public SocketChannel bind(SocketAddress local) throws IOException {
synchronized (readLock) {
synchronized (writeLock) {
synchronized (stateLock) {
ensureOpenAndUnconnected();
if (!isOpen())
throw new ClosedChannelException();
if (state == ST_PENDING)
throw new ConnectionPendingException();
if (localAddress != null)
throw new AlreadyBoundException();
InetSocketAddress isa = Net.checkAddress(local);
InetSocketAddress isa = (local == null) ?
new InetSocketAddress(0) : Net.checkAddress(local);
Net.bind(fd, isa.getAddress(), isa.getPort());
localAddress = Net.localAddress(fd);
}
}
}
return this;
}
public boolean isConnected() {
@ -496,7 +558,6 @@ class SocketChannelImpl
}
public boolean connect(SocketAddress sa) throws IOException {
int trafficClass = 0; // ## Pick up from options
int localPort = 0;
synchronized (readLock) {
@ -524,13 +585,24 @@ class SocketChannelImpl
ia = InetAddress.getLocalHost();
n = Net.connect(fd,
ia,
isa.getPort(),
trafficClass);
isa.getPort());
if ( (n == IOStatus.INTERRUPTED)
&& isOpen())
continue;
break;
}
synchronized (stateLock) {
if (isOpen() && (localAddress == null) ||
((InetSocketAddress)localAddress)
.getAddress().isAnyLocalAddress())
{
// Socket was not bound before connecting or
// Socket was bound with an "anyLocalAddress"
localAddress = Net.localAddress(fd);
}
}
} finally {
readerCleanup();
end((n > 0) || (n == IOStatus.UNAVAILABLE));
@ -646,29 +718,37 @@ class SocketChannelImpl
}
}
public final static int SHUT_RD = 0;
public final static int SHUT_WR = 1;
public final static int SHUT_RDWR = 2;
public void shutdownInput() throws IOException {
@Override
public SocketChannel shutdownInput() throws IOException {
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
isInputOpen = false;
shutdown(fd, SHUT_RD);
if (!isConnected())
throw new NotYetConnectedException();
if (isInputOpen) {
Net.shutdown(fd, Net.SHUT_RD);
if (readerThread != 0)
NativeThread.signal(readerThread);
isInputOpen = false;
}
return this;
}
}
public void shutdownOutput() throws IOException {
@Override
public SocketChannel shutdownOutput() throws IOException {
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
isOutputOpen = false;
shutdown(fd, SHUT_WR);
if (!isConnected())
throw new NotYetConnectedException();
if (isOutputOpen) {
Net.shutdown(fd, Net.SHUT_WR);
if (writerThread != 0)
NativeThread.signal(writerThread);
isOutputOpen = false;
}
return this;
}
}
@ -869,9 +949,6 @@ class SocketChannelImpl
boolean block, boolean ready)
throws IOException;
private static native void shutdown(FileDescriptor fd, int how)
throws IOException;
static {
Util.load();
nd = new SocketDispatcher();

View file

@ -1,115 +0,0 @@
/*
* Copyright 2001 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import java.io.IOException;
import java.nio.*;
import java.net.NetworkInterface;
// Typical use:
//
// sc.options()
// .noDelay(true)
// .typeOfService(SocketOpts.IP.TOS_RELIABILITY)
// .sendBufferSize(1024)
// .receiveBufferSize(1024)
// .keepAlive(true);
//
public interface SocketOpts { // SocketOptions already used in java.net
// Options that apply to all kinds of sockets
// SO_BROADCAST
public abstract boolean broadcast() throws IOException;
public abstract SocketOpts broadcast(boolean b) throws IOException;
// SO_KEEPALIVE
public abstract boolean keepAlive() throws IOException;
public abstract SocketOpts keepAlive(boolean b) throws IOException;
// SO_LINGER
public abstract int linger() throws IOException;
public abstract SocketOpts linger(int n) throws IOException;
// SO_OOBINLINE
public abstract boolean outOfBandInline() throws IOException;
public abstract SocketOpts outOfBandInline(boolean b) throws IOException;
// SO_RCVBUF
public abstract int receiveBufferSize() throws IOException;
public abstract SocketOpts receiveBufferSize(int n) throws IOException;
// SO_SNDBUF
public abstract int sendBufferSize() throws IOException;
public abstract SocketOpts sendBufferSize(int n) throws IOException;
// SO_REUSEADDR
public abstract boolean reuseAddress() throws IOException;
public abstract SocketOpts reuseAddress(boolean b) throws IOException;
// IP-specific options
public static interface IP
extends SocketOpts
{
// IP_MULTICAST_IF2
public abstract NetworkInterface multicastInterface()
throws IOException;
public abstract IP multicastInterface(NetworkInterface ni)
throws IOException;
// IP_MULTICAST_LOOP
public abstract boolean multicastLoop() throws IOException;
public abstract IP multicastLoop(boolean b) throws IOException;
// IP_TOS
public static final int TOS_LOWDELAY = 0x10;
public static final int TOS_THROUGHPUT = 0x08;
public static final int TOS_RELIABILITY = 0x04;
public static final int TOS_MINCOST = 0x02;
public abstract int typeOfService() throws IOException;
public abstract IP typeOfService(int tos) throws IOException;
// TCP-specific options
public static interface TCP
extends IP
{
// TCP_NODELAY
public abstract boolean noDelay() throws IOException;
public abstract TCP noDelay(boolean b) throws IOException;
}
}
}

View file

@ -1,318 +0,0 @@
/*
* Copyright 2001 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package sun.nio.ch;
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.NetworkInterface;
import java.net.SocketOptions;
import java.nio.channels.*;
class SocketOptsImpl
implements SocketOpts
{
static abstract class Dispatcher {
abstract int getInt(int opt) throws IOException;
abstract void setInt(int opt, int arg) throws IOException;
// Others that pass addresses, etc., will come later
}
private final Dispatcher d;
SocketOptsImpl(Dispatcher d) {
this.d = d;
}
protected boolean getBoolean(int opt) throws IOException {
return d.getInt(opt) > 0;
}
protected void setBoolean(int opt, boolean b) throws IOException {
d.setInt(opt, b ? 1 : 0);
}
protected int getInt(int opt) throws IOException {
return d.getInt(opt);
}
protected void setInt(int opt, int n) throws IOException {
d.setInt(opt, n);
}
protected NetworkInterface getNetworkInterface(int opt)
throws IOException
{
throw new UnsupportedOperationException("NYI");
}
protected void setNetworkInterface(int opt, NetworkInterface ni)
throws IOException
{
throw new UnsupportedOperationException("NYI");
}
protected void addToString(StringBuffer sb, String s) {
char c = sb.charAt(sb.length() - 1);
if ((c != '[') && (c != '='))
sb.append(' ');
sb.append(s);
}
protected void addToString(StringBuffer sb, int n) {
addToString(sb, Integer.toString(n));
}
// SO_BROADCAST
public boolean broadcast() throws IOException {
return getBoolean(SocketOptions.SO_BROADCAST);
}
public SocketOpts broadcast(boolean b) throws IOException {
setBoolean(SocketOptions.SO_BROADCAST, b);
return this;
}
// SO_KEEPALIVE
public boolean keepAlive() throws IOException {
return getBoolean(SocketOptions.SO_KEEPALIVE);
}
public SocketOpts keepAlive(boolean b) throws IOException {
setBoolean(SocketOptions.SO_KEEPALIVE, b);
return this;
}
// SO_LINGER
public int linger() throws IOException {
return getInt(SocketOptions.SO_LINGER);
}
public SocketOpts linger(int n) throws IOException {
setInt(SocketOptions.SO_LINGER, n);
return this;
}
// SO_OOBINLINE
public boolean outOfBandInline() throws IOException {
return getBoolean(SocketOptions.SO_OOBINLINE);
}
public SocketOpts outOfBandInline(boolean b) throws IOException {
setBoolean(SocketOptions.SO_OOBINLINE, b);
return this;
}
// SO_RCVBUF
public int receiveBufferSize() throws IOException {
return getInt(SocketOptions.SO_RCVBUF);
}
public SocketOpts receiveBufferSize(int n) throws IOException {
if (n <= 0)
throw new IllegalArgumentException("Invalid receive size");
setInt(SocketOptions.SO_RCVBUF, n);
return this;
}
// SO_SNDBUF
public int sendBufferSize() throws IOException {
return getInt(SocketOptions.SO_SNDBUF);
}
public SocketOpts sendBufferSize(int n) throws IOException {
if (n <= 0)
throw new IllegalArgumentException("Invalid send size");
setInt(SocketOptions.SO_SNDBUF, n);
return this;
}
// SO_REUSEADDR
public boolean reuseAddress() throws IOException {
return getBoolean(SocketOptions.SO_REUSEADDR);
}
public SocketOpts reuseAddress(boolean b) throws IOException {
setBoolean(SocketOptions.SO_REUSEADDR, b);
return this;
}
// toString
protected void toString(StringBuffer sb) throws IOException {
int n;
if (broadcast())
addToString(sb, "broadcast");
if (keepAlive())
addToString(sb, "keepalive");
if ((n = linger()) > 0) {
addToString(sb, "linger=");
addToString(sb, n);
}
if (outOfBandInline())
addToString(sb, "oobinline");
if ((n = receiveBufferSize()) > 0) {
addToString(sb, "rcvbuf=");
addToString(sb, n);
}
if ((n = sendBufferSize()) > 0) {
addToString(sb, "sndbuf=");
addToString(sb, n);
}
if (reuseAddress())
addToString(sb, "reuseaddr");
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(this.getClass().getInterfaces()[0].getName());
sb.append('[');
int i = sb.length();
try {
toString(sb);
} catch (IOException x) {
sb.setLength(i);
sb.append("closed");
}
sb.append(']');
return sb.toString();
}
// IP-specific socket options
static class IP
extends SocketOptsImpl
implements SocketOpts.IP
{
IP(Dispatcher d) {
super(d);
}
// IP_MULTICAST_IF2
// ## Do we need IP_MULTICAST_IF also?
public NetworkInterface multicastInterface() throws IOException {
return getNetworkInterface(SocketOptions.IP_MULTICAST_IF2);
}
public SocketOpts.IP multicastInterface(NetworkInterface ni)
throws IOException
{
setNetworkInterface(SocketOptions.IP_MULTICAST_IF2, ni);
return this;
}
// IP_MULTICAST_LOOP
public boolean multicastLoop() throws IOException {
return getBoolean(SocketOptions.IP_MULTICAST_LOOP);
}
public SocketOpts.IP multicastLoop(boolean b) throws IOException {
setBoolean(SocketOptions.IP_MULTICAST_LOOP, b);
return this;
}
// IP_TOS
public int typeOfService() throws IOException {
return getInt(SocketOptions.IP_TOS);
}
public SocketOpts.IP typeOfService(int tos) throws IOException {
setInt(SocketOptions.IP_TOS, tos);
return this;
}
// toString
protected void toString(StringBuffer sb) throws IOException {
super.toString(sb);
int n;
if ((n = typeOfService()) > 0) {
addToString(sb, "tos=");
addToString(sb, n);
}
}
// TCP-specific IP options
public static class TCP
extends SocketOptsImpl.IP
implements SocketOpts.IP.TCP
{
TCP(Dispatcher d) {
super(d);
}
// TCP_NODELAY
public boolean noDelay() throws IOException {
return getBoolean(SocketOptions.TCP_NODELAY);
}
public SocketOpts.IP.TCP noDelay(boolean b) throws IOException {
setBoolean(SocketOptions.TCP_NODELAY, b);
return this;
}
// toString
protected void toString(StringBuffer sb) throws IOException {
super.toString(sb);
if (noDelay())
addToString(sb, "nodelay");
}
}
}
}

View file

@ -82,7 +82,7 @@ void init(JNIEnv *env) {
}
}
jobject
JNIEXPORT jobject JNICALL
NET_SockaddrToInetAddress(JNIEnv *env, struct sockaddr *him, int *port) {
jobject iaObj;
init(env);
@ -159,7 +159,7 @@ NET_SockaddrToInetAddress(JNIEnv *env, struct sockaddr *him, int *port) {
return iaObj;
}
jint
JNIEXPORT jint JNICALL
NET_SockaddrEqualsInetAddress(JNIEnv *env, struct sockaddr *him, jobject iaObj)
{
jint family = (*env)->GetIntField(env, iaObj, ia_familyID) == IPv4?

View file

@ -116,7 +116,7 @@ NET_AllocSockaddr(struct sockaddr **him, int *len);
JNIEXPORT int JNICALL
NET_InetAddressToSockaddr(JNIEnv *env, jobject iaObj, int port, struct sockaddr *him, int *len, jboolean v4MappedAddress);
jobject
JNIEXPORT jobject JNICALL
NET_SockaddrToInetAddress(JNIEnv *env, struct sockaddr *him, int *port);
void initLocalAddrTable ();
@ -124,10 +124,10 @@ void initLocalAddrTable ();
void
NET_SetTrafficClass(struct sockaddr *him, int trafficClass);
jint
JNIEXPORT jint JNICALL
NET_GetPortFromSockaddr(struct sockaddr *him);
jint
JNIEXPORT jint JNICALL
NET_SockaddrEqualsInetAddress(JNIEnv *env,struct sockaddr *him, jobject iaObj);
int

Some files were not shown because too many files have changed in this diff Show more