mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-03 15:51:08 +00:00
Merge
This commit is contained in:
commit
710733eaa5
@ -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 \
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -56,18 +56,18 @@ FILES_java += \
|
||||
sun/nio/ch/DevPollSelectorProvider.java \
|
||||
sun/nio/ch/InheritedChannel.java \
|
||||
sun/nio/ch/PollSelectorProvider.java \
|
||||
sun/nio/ch/PollSelectorImpl.java
|
||||
sun/nio/ch/PollSelectorImpl.java
|
||||
|
||||
FILES_c += \
|
||||
DevPollArrayWrapper.c \
|
||||
InheritedChannel.c \
|
||||
PollArrayWrapper.c \
|
||||
NativeThread.c
|
||||
NativeThread.c \
|
||||
PollArrayWrapper.c
|
||||
|
||||
FILES_export += \
|
||||
sun/nio/ch/DevPollArrayWrapper.java \
|
||||
sun/nio/ch/InheritedChannel.java \
|
||||
sun/nio/ch/NativeThread.java
|
||||
sun/nio/ch/NativeThread.java
|
||||
endif # PLATFORM = solaris
|
||||
|
||||
ifeq ($(PLATFORM), windows)
|
||||
@ -94,14 +94,14 @@ FILES_java += \
|
||||
|
||||
FILES_c += \
|
||||
EPollArrayWrapper.c \
|
||||
PollArrayWrapper.c \
|
||||
InheritedChannel.c \
|
||||
NativeThread.c
|
||||
NativeThread.c \
|
||||
PollArrayWrapper.c
|
||||
|
||||
FILES_export += \
|
||||
sun/nio/ch/EPollArrayWrapper.java \
|
||||
sun/nio/ch/InheritedChannel.java \
|
||||
sun/nio/ch/NativeThread.java
|
||||
sun/nio/ch/NativeThread.java
|
||||
endif # PLATFORM = linux
|
||||
|
||||
# Find platform-specific C source files
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
*;
|
||||
|
||||
@ -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:
|
||||
*;
|
||||
|
||||
@ -31,7 +31,7 @@ BUILDDIR = ../..
|
||||
PRODUCT = java
|
||||
include $(BUILDDIR)/common/Defs.gmk
|
||||
|
||||
SUBDIRS = server
|
||||
SUBDIRS = multicast server
|
||||
all build clean clobber::
|
||||
$(SUBDIRS-loop)
|
||||
|
||||
|
||||
@ -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
|
||||
@ -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.
|
||||
*/
|
||||
|
||||
@ -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.
|
||||
@ -961,16 +965,17 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
|
||||
|
||||
if (logicalName != name && logicalName != null) {
|
||||
logicalName =
|
||||
ObjectName.getInstance(nonDefaultDomain(logicalName));
|
||||
ObjectName.getInstance(nonDefaultDomain(logicalName));
|
||||
}
|
||||
|
||||
checkMBeanPermission(classname, null, logicalName, "registerMBean");
|
||||
checkMBeanPermission(mbeanServerName, classname, null, logicalName,
|
||||
"registerMBean");
|
||||
|
||||
if (logicalName == null) {
|
||||
final RuntimeException wrapped =
|
||||
new IllegalArgumentException("No object name specified");
|
||||
new IllegalArgumentException("No object name specified");
|
||||
throw new RuntimeOperationsException(wrapped,
|
||||
"Exception occurred trying to register the MBean");
|
||||
"Exception occurred trying to register the MBean");
|
||||
}
|
||||
|
||||
final Object resource = getResource(mbean);
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
@Deprecated
|
||||
public ObjectInputStream deserialize(String className,
|
||||
ObjectName loaderName, byte[] data)
|
||||
throws InstanceNotFoundException, OperationsException,
|
||||
ReflectionException;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* This method should never be called.
|
||||
* Usually throws UnsupportedOperationException.
|
||||
*/
|
||||
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,
|
||||
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.
|
||||
*/
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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.");
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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.
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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>();
|
||||
|
||||
|
||||
@ -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,
|
||||
@ -370,7 +289,7 @@ public class Repository {
|
||||
if (name.isPattern()) return null;
|
||||
|
||||
// Extract the domain name.
|
||||
String dom= name.getDomain().intern();
|
||||
String dom = name.getDomain().intern();
|
||||
|
||||
// Default domain case
|
||||
if (dom.length() == 0) {
|
||||
@ -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>());
|
||||
}
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
282
jdk/src/share/classes/com/sun/jmx/namespace/RoutingProxy.java
Normal file
282
jdk/src/share/classes/com/sun/jmx/namespace/RoutingProxy.java
Normal 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));
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
45
jdk/src/share/classes/com/sun/jmx/namespace/package.html
Normal file
45
jdk/src/share/classes/com/sun/jmx/namespace/package.html
Normal 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>
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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>
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -25,7 +25,6 @@
|
||||
|
||||
package java.net;
|
||||
|
||||
import java.net.SocketException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.NoSuchElementException;
|
||||
import sun.security.action.*;
|
||||
|
||||
39
jdk/src/share/classes/java/net/ProtocolFamily.java
Normal file
39
jdk/src/share/classes/java/net/ProtocolFamily.java
Normal 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();
|
||||
}
|
||||
55
jdk/src/share/classes/java/net/SocketOption.java
Normal file
55
jdk/src/share/classes/java/net/SocketOption.java
Normal 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();
|
||||
}
|
||||
45
jdk/src/share/classes/java/net/StandardProtocolFamily.java
Normal file
45
jdk/src/share/classes/java/net/StandardProtocolFamily.java
Normal 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
|
||||
}
|
||||
352
jdk/src/share/classes/java/net/StandardSocketOption.java
Normal file
352
jdk/src/share/classes/java/net/StandardSocketOption.java
Normal 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 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 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 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 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 1349</a>
|
||||
* and <a href="http://www.ietf.org/rfc/rfc2474.txt">RFC 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 <= value <= 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 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; }
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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,14 +245,16 @@ 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,
|
||||
offset << $LG_BYTES_PER_VALUE$,
|
||||
length << $LG_BYTES_PER_VALUE$);
|
||||
#end[!byte]
|
||||
Bits.copyToArray(ix(pos), dst, arrayBaseOffset,
|
||||
offset << $LG_BYTES_PER_VALUE$,
|
||||
length << $LG_BYTES_PER_VALUE$);
|
||||
position(pos + length);
|
||||
} else {
|
||||
super.get(dst, offset, length);
|
||||
@ -332,12 +337,14 @@ 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$,
|
||||
ix(pos), length << $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 {
|
||||
super.put(src, offset, length);
|
||||
|
||||
@ -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.
|
||||
*
|
||||
|
||||
183
jdk/src/share/classes/java/nio/channels/MembershipKey.java
Normal file
183
jdk/src/share/classes/java/nio/channels/MembershipKey.java
Normal 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();
|
||||
}
|
||||
211
jdk/src/share/classes/java/nio/channels/MulticastChannel.java
Normal file
211
jdk/src/share/classes/java/nio/channels/MulticastChannel.java
Normal 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 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 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 2710:
|
||||
* Multicast Listener Discovery (MLD) for IPv6</i></a> and <a
|
||||
* href="http://www.ietf.org/rfc/rfc3810.txt"> <i>RFC 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;
|
||||
}
|
||||
158
jdk/src/share/classes/java/nio/channels/NetworkChannel.java
Normal file
158
jdk/src/share/classes/java/nio/channels/NetworkChannel.java
Normal 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();
|
||||
}
|
||||
@ -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.
|
||||
*
|
||||
|
||||
@ -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 --
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
231
jdk/src/share/classes/java/nio/channels/package-info.java
Normal file
231
jdk/src/share/classes/java/nio/channels/package-info.java
Normal 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> <i>{@link java.nio.channels.ReadableByteChannel}</i></tt></td>
|
||||
* <td>Can read into a buffer</td></tr>
|
||||
* <tr><td valign=top><tt> <i>{@link java.nio.channels.ScatteringByteChannel} </i></tt></td>
|
||||
* <td>Can read into a sequence of buffers</td></tr>
|
||||
* <tr><td valign=top><tt> <i>{@link java.nio.channels.WritableByteChannel}</i></tt></td>
|
||||
* <td>Can write from a buffer</td></tr>
|
||||
* <tr><td valign=top><tt> <i>{@link java.nio.channels.GatheringByteChannel}</i></tt></td>
|
||||
* <td>Can write from a sequence of buffers</td></tr>
|
||||
* <tr><td valign=top><tt> <i>{@link java.nio.channels.ByteChannel}</i></tt></td>
|
||||
* <td>Can read/write to/from a buffer</td></tr>
|
||||
* <tr><td valign=top><tt> <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> <i>{@link java.nio.channels.NetworkChannel}</i></tt></td>
|
||||
* <td>A channel to a network socket</td></tr>
|
||||
* <tr><td valign=top><tt> <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} </tt></td>
|
||||
* <td>A direct byte buffer or big byte buffer mapped to a region of a 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> {@link java.nio.channels.DatagramChannel}</tt></td>
|
||||
* <td>A channel to a datagram-oriented socket</td></tr>
|
||||
* <tr><td valign=top><tt> {@link java.nio.channels.Pipe.SinkChannel}</tt></td>
|
||||
* <td>The write end of a pipe</td></tr>
|
||||
* <tr><td valign=top><tt> {@link java.nio.channels.Pipe.SourceChannel}</tt></td>
|
||||
* <td>The read end of a pipe</td></tr>
|
||||
* <tr><td valign=top><tt> {@link java.nio.channels.ServerSocketChannel} </tt></td>
|
||||
* <td>A channel to a stream-oriented listening socket</td></tr>
|
||||
* <tr><td valign=top><tt> {@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 a selector</td></tr>
|
||||
* <tr><td valign=top><tt>{@link java.nio.channels.Pipe}</tt></td>
|
||||
* <td>Two channels that form a unidirectional 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;
|
||||
@ -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> <i>{@link java.nio.channels.ReadableByteChannel}</i></tt></td>
|
||||
<td>Can read into a buffer</td></tr>
|
||||
<tr><td valign=top><tt> <i>{@link java.nio.channels.ScatteringByteChannel} </i></tt></td>
|
||||
<td>Can read into a sequence of buffers</td></tr>
|
||||
<tr><td valign=top><tt> <i>{@link java.nio.channels.WritableByteChannel}</i></tt></td>
|
||||
<td>Can write from a buffer</td></tr>
|
||||
<tr><td valign=top><tt> <i>{@link java.nio.channels.GatheringByteChannel}</i></tt></td>
|
||||
<td>Can write from a sequence of buffers</td></tr>
|
||||
<tr><td valign=top><tt> <i>{@link java.nio.channels.ByteChannel}</i></tt></td>
|
||||
<td>Can read/write to/from a 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} </tt></td>
|
||||
<td>A direct byte buffer mapped to a region of a 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> {@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> {@link java.nio.channels.Pipe.SinkChannel}</tt></td>
|
||||
<td>The write end of a pipe</td></tr>
|
||||
<tr><td valign=top><tt> {@link java.nio.channels.Pipe.SourceChannel}</tt></td>
|
||||
<td>The read end of a pipe</td></tr>
|
||||
<tr><td valign=top><tt> {@link java.nio.channels.ServerSocketChannel} </tt></td>
|
||||
<td>A channel for a {@link java.net.ServerSocket java.net.ServerSocket}</td></tr>
|
||||
<tr><td valign=top><tt> {@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 a selector</td></tr>
|
||||
<tr><td valign=top><tt>{@link java.nio.channels.Pipe}</tt></td>
|
||||
<td>Two channels that form a unidirectional 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>
|
||||
@ -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;
|
||||
@ -190,7 +188,25 @@ public abstract class SelectorProvider {
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Opens a pipe. </p>
|
||||
* 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>
|
||||
*
|
||||
* @return The new pipe
|
||||
*/
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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[],
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
@ -316,20 +509,50 @@ public class MBeanServerFactory {
|
||||
|
||||
synchronized(mbsBuilder) {
|
||||
final MBeanServerDelegate delegate =
|
||||
mbsBuilder.newMBeanServerDelegate();
|
||||
mbsBuilder.newMBeanServerDelegate();
|
||||
if (delegate == null) {
|
||||
final String msg =
|
||||
"MBeanServerBuilder.newMBeanServerDelegate() " +
|
||||
"returned null";
|
||||
"MBeanServerBuilder.newMBeanServerDelegate() " +
|
||||
"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);
|
||||
mbsBuilder.newMBeanServer(domain,null,delegate);
|
||||
if (mbeanServer == null) {
|
||||
final String msg =
|
||||
"MBeanServerBuilder.newMBeanServer() returned null";
|
||||
"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
|
||||
@ -387,21 +701,23 @@ public class MBeanServerFactory {
|
||||
*
|
||||
**/
|
||||
public static ClassLoaderRepository getClassLoaderRepository(
|
||||
MBeanServer server) {
|
||||
MBeanServer server) {
|
||||
return server.getClassLoaderRepository();
|
||||
}
|
||||
|
||||
private static String mBeanServerName(MBeanServer mbs) {
|
||||
private static String mBeanServerId(MBeanServer mbs) {
|
||||
try {
|
||||
return (String) mbs.getAttribute(MBeanServerDelegate.DELEGATE_NAME,
|
||||
"MBeanServerId");
|
||||
"MBeanServerId");
|
||||
} catch (JMException e) {
|
||||
JmxProperties.MISC_LOGGER.finest(
|
||||
"Ignoring exception while getting MBeanServerId: "+e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkPermission(String action)
|
||||
throws SecurityException {
|
||||
throws SecurityException {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
Permission perm = new MBeanServerPermission(action);
|
||||
@ -425,16 +741,16 @@ public class MBeanServerFactory {
|
||||
}
|
||||
|
||||
private static final ArrayList<MBeanServer> mBeanServerList =
|
||||
new ArrayList<MBeanServer>();
|
||||
new ArrayList<MBeanServer>();
|
||||
|
||||
/**
|
||||
* Load the builder class through the context class loader.
|
||||
* @param builderClassName The name of the builder class.
|
||||
**/
|
||||
private static Class loadBuilderClass(String builderClassName)
|
||||
throws ClassNotFoundException {
|
||||
throws ClassNotFoundException {
|
||||
final ClassLoader loader =
|
||||
Thread.currentThread().getContextClassLoader();
|
||||
Thread.currentThread().getContextClassLoader();
|
||||
|
||||
if (loader != null) {
|
||||
// Try with context class loader
|
||||
@ -453,14 +769,14 @@ 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) {
|
||||
final String msg =
|
||||
"Failed to instantiate a MBeanServerBuilder from " +
|
||||
builderClass + ": " + x;
|
||||
"Failed to instantiate a MBeanServerBuilder from " +
|
||||
builderClass + ": " + x;
|
||||
throw new JMRuntimeException(msg, x);
|
||||
}
|
||||
}
|
||||
@ -472,7 +788,7 @@ public class MBeanServerFactory {
|
||||
private static synchronized void checkMBeanServerBuilder() {
|
||||
try {
|
||||
GetPropertyAction act =
|
||||
new GetPropertyAction(JMX_INITIAL_BUILDER);
|
||||
new GetPropertyAction(JMX_INITIAL_BUILDER);
|
||||
String builderClassName = AccessController.doPrivileged(act);
|
||||
|
||||
try {
|
||||
@ -493,8 +809,8 @@ public class MBeanServerFactory {
|
||||
builder = newBuilder(newBuilderClass);
|
||||
} catch (ClassNotFoundException x) {
|
||||
final String msg =
|
||||
"Failed to load MBeanServerBuilder class " +
|
||||
builderClassName + ": " + x;
|
||||
"Failed to load MBeanServerBuilder class " +
|
||||
builderClassName + ": " + x;
|
||||
throw new JMRuntimeException(msg, x);
|
||||
}
|
||||
} catch (RuntimeException x) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
// ------------------------------------
|
||||
|
||||
382
jdk/src/share/classes/javax/management/namespace/JMXDomain.java
Normal file
382
jdk/src/share/classes/javax/management/namespace/JMXDomain.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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(<mbean server name>, <operation name>, <namespace>//<mbean>, "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(<mbean server name>, <attribute>, <namespace>//<mbean>, "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(<mbean server name>, <null>, <namespace>//<mbean>, "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(<mbean server name>, <em>att</em>,
|
||||
* <namespace>//<mbean>, "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(<mbean server name>, <attrName>, <namespace>//<mbean>, "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(<mbean server name>, null, <namespace>//<mbean>, "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(<mbean server name>, <em>att</em>, <namespace>//<mbean>, "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(<mbean server name>, null, <namespace>//<mbean>,
|
||||
* "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(<mbean server name>, null, <namespace>//<mbean>,
|
||||
* "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(<mbean server name>, null, <namespace>//<mbean>,
|
||||
* "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(<mbean server name>, null, <namespace>//<mbean>,
|
||||
* "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(<mbean server name>, null, <namespace>//<mbean>,
|
||||
* "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(<mbean server name>, 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(<mbean server name>, null, <namespace>//<mbean>,
|
||||
* "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(<mbean server name>, null, <namespace>//<loaderName>,
|
||||
* "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(<mbean server name>, null, <namespace>//<mbean>,
|
||||
* "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(<mbean server name>, <class name>, <namespace>//<mbean>,
|
||||
* "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(<mbean server name>, <class name>, <namespace>//<mbean>,
|
||||
* "instantiate")} and
|
||||
* JMXNamespacePermission#JMXNamespacePermission(String,String,ObjectName,String)
|
||||
* JMXNamespacePermission(<mbean server name>, <class name>, <namespace>//<mbean>,
|
||||
* "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(<mbean server name>, null, <namespace>//<mbean>,
|
||||
* "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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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 /");
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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
|
||||
//----------------
|
||||
|
||||
}
|
||||
@ -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);
|
||||
@ -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");
|
||||
}
|
||||
@ -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<String,Object> 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("*//:*",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;
|
||||
|
||||
@ -89,7 +89,7 @@ public class CompositeType extends OpenType<CompositeData> {
|
||||
* <br>
|
||||
* @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>
|
||||
* @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");
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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 @{@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
|
||||
|
||||
@ -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());
|
||||
@ -1392,7 +1424,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
|
||||
if (notifAC != null)
|
||||
notifAC.removeNotificationListener(connectionId, name, getSubject());
|
||||
try {
|
||||
delegate.removeListenerOrSubscriber(clientId,id);
|
||||
delegate.removeListenerOrSubscriber(clientId, id);
|
||||
} catch (EventClientNotFoundException x) {
|
||||
throw new IOException("Unknown clientId: "+clientId,x);
|
||||
}
|
||||
@ -1405,7 +1437,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
|
||||
notifAC.removeNotificationListener(connectionId, name, getSubject());
|
||||
try {
|
||||
for (Integer id : ids)
|
||||
delegate.removeListenerOrSubscriber(clientId,id);
|
||||
delegate.removeListenerOrSubscriber(clientId, id);
|
||||
} catch (EventClientNotFoundException x) {
|
||||
throw new IOException("Unknown clientId: "+clientId,x);
|
||||
}
|
||||
@ -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];
|
||||
|
||||
|
||||
@ -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,8 +1070,8 @@ class DatagramChannelImpl
|
||||
boolean connected)
|
||||
throws IOException;
|
||||
|
||||
private native int send0(FileDescriptor fd, long address, int len,
|
||||
SocketAddress sa)
|
||||
private native int send0(boolean preferIPv6, FileDescriptor fd, long address, int len,
|
||||
SocketAddress sa)
|
||||
throws IOException;
|
||||
|
||||
static {
|
||||
|
||||
@ -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;
|
||||
try {
|
||||
return Net.asInetSocketAddress(dc.localAddress()).getAddress();
|
||||
} catch (Exception x) {
|
||||
return new InetSocketAddress(0).getAddress();
|
||||
SocketAddress local = dc.localAddress();
|
||||
if (local == null)
|
||||
local = new InetSocketAddress(0);
|
||||
InetAddress result = ((InetSocketAddress)local).getAddress();
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
try {
|
||||
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();
|
||||
SocketAddress local = dc.getLocalAddress();
|
||||
if (local != null) {
|
||||
return ((InetSocketAddress)local).getPort();
|
||||
}
|
||||
} catch (Exception x) {
|
||||
return 0;
|
||||
}
|
||||
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() {
|
||||
|
||||
44
jdk/src/share/classes/sun/nio/ch/ExtendedSocketOption.java
Normal file
44
jdk/src/share/classes/sun/nio/ch/ExtendedSocketOption.java
Normal 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(); }
|
||||
};
|
||||
}
|
||||
222
jdk/src/share/classes/sun/nio/ch/MembershipKeyImpl.java
Normal file
222
jdk/src/share/classes/sun/nio/ch/MembershipKeyImpl.java
Normal 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();
|
||||
}
|
||||
}
|
||||
129
jdk/src/share/classes/sun/nio/ch/MembershipRegistry.java
Normal file
129
jdk/src/share/classes/sun/nio/ch/MembershipRegistry.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
InetAddress remote,
|
||||
int remotePort,
|
||||
int trafficClass)
|
||||
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)
|
||||
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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
48
jdk/src/share/classes/sun/nio/ch/OptionKey.java
Normal file
48
jdk/src/share/classes/sun/nio/ch/OptionKey.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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())
|
||||
return new InetSocketAddress(0).getAddress();
|
||||
return Net.asInetSocketAddress(sc.localAddress()).getAddress();
|
||||
if (sc.isOpen()) {
|
||||
SocketAddress local = sc.localAddress();
|
||||
if (local != null)
|
||||
return ((InetSocketAddress)local).getAddress();
|
||||
}
|
||||
return new InetSocketAddress(0).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() {
|
||||
|
||||
@ -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 (readerThread != 0)
|
||||
NativeThread.signal(readerThread);
|
||||
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 (writerThread != 0)
|
||||
NativeThread.signal(writerThread);
|
||||
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();
|
||||
|
||||
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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?
|
||||
|
||||
@ -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
Loading…
x
Reference in New Issue
Block a user