All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.xnio.Xnio Maven / Gradle / Ivy

There is a newer version: 3.8.16.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2008, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.xnio;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.security.AccessController;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Permission;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Properties;
import java.util.HashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.net.InetSocketAddress;
import org.jboss.xnio.channels.BoundChannel;
import org.jboss.xnio.channels.Channels;
import org.jboss.xnio.channels.ConnectedStreamChannel;
import org.jboss.xnio.channels.DatagramChannel;
import org.jboss.xnio.channels.SslTcpChannel;
import org.jboss.xnio.channels.StreamChannel;
import org.jboss.xnio.channels.StreamSinkChannel;
import org.jboss.xnio.channels.StreamSourceChannel;
import org.jboss.xnio.channels.TcpChannel;
import org.jboss.xnio.channels.UdpChannel;
import org.jboss.xnio.log.Logger;
import org.jboss.xnio.management.OneWayPipeConnectionMBean;
import org.jboss.xnio.management.PipeConnectionMBean;
import org.jboss.xnio.management.PipeServerMBean;
import org.jboss.xnio.management.PipeSinkServerMBean;
import org.jboss.xnio.management.PipeSourceServerMBean;
import org.jboss.xnio.management.TcpConnectionMBean;
import org.jboss.xnio.management.TcpServerMBean;
import org.jboss.xnio.management.UdpServerMBean;

import javax.management.InstanceNotFoundException;
import javax.management.JMException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.RuntimeOperationsException;
import javax.net.SocketFactory;
import javax.net.ServerSocketFactory;
import javax.net.ssl.SSLContext;

/**
 * The XNIO entry point class.
 *
 * @apiviz.landmark
 */
public abstract class Xnio implements Closeable {

    private static final Logger mlog = Logger.getLogger("org.jboss.xnio.management");

    static {
        Logger.getLogger("org.jboss.xnio").info("XNIO Version " + Version.VERSION);
    }

    private static final String NIO_IMPL_PROVIDER = "nio";
    private static final String PROVIDER_NAME;
    private static final String MANAGEMENT_DOMAIN = "jboss.xnio";
    private static final String PROPERTIES = "xnio.properties";

    private static final AtomicLong mbeanSequence = new AtomicLong();

    private static String AGENTID_PROPNAME = "xnio.agentid";
    private static String PROVIDER_PROPNAME = "xnio.provider.name";
    private static String PROPERTY_FILE_PROPNAME = "xnio.property.file";

    private static final PrivilegedAction GET_PROVIDER_ACTION = new GetPropertyAction(PROVIDER_PROPNAME, NIO_IMPL_PROVIDER);
    private static final PrivilegedAction GET_AGENTID_ACTION = new GetPropertyAction(AGENTID_PROPNAME, null);

    private static final Permission SUBCLASS_PERMISSION = new RuntimePermission("xnioProvider");

    static {
        String providerClassName = NIO_IMPL_PROVIDER;
        try {
            providerClassName = AccessController.doPrivileged(GET_PROVIDER_ACTION);
        } catch (Throwable t) {
            // ignored
        }
        PROVIDER_NAME = providerClassName;
    }

    private final List mBeanServers = new ArrayList();

    private final String name;
    private final Executor executor;

    /**
     * Get the default listener executor.
     *
     * @return the executor
     */
    protected Executor getExecutor() {
        return executor;
    }

    private static final Map instanceMap = new HashMap();

    /**
     * Get or create a named XNIO provider instance, which is automatically configured from a properties file.
     * 

* The boot classpath is searched for a file named {@code "xnio.properties"} (the name can be overridden by way of * the {@code "xnio.property.file"} system property). This file contains properties which are used to configure * all created providers. *

* The following properties are recognized: *

    *
  • <name>.listener.threadpool - a boolean value which specifies whether channel listeners should be invoked via * a thread pool executor. A value of {@code true} indicates that a thread pool should be created; a value of * {@code false} (the default) indicates that the listeners should be invoked from the current thread.
  • *
  • <name>.listener.threadpool.coresize - an integer value which specifies the core size of the thread pool. * The default value is 8 threads.
  • *
  • <name>.listener.threadpool.maxsize - an integer value which specifies the maximum size of the thread pool. * The default value is 64 threads.
  • *
  • <name>.listener.threadpool.keepaliveseconds - an integer value which specifies the number of seconds an idle * thread should be kept alive before exiting. The default value is 30 seconds.
  • *
  • <name>.listener.threadpool.queuelength - an integer value which specifies the length of the task queue for the * listener thread pool. The default value is 64.
  • *
  • <name>.provider.option.<option-name> - An option to add to the XNIO provider's option map. The * value is the value for the option.
  • *
  • <name>.provider - the provider implementation to use. If not specified, a default provider will be located and * used.
  • *
* * @param name the provider name to get * @return the configured global XNIO instance * @throws IOException if the XNIO provider could not be created */ public static Xnio getInstance(final String name) throws IOException { synchronized (instanceMap) { final Xnio instance = instanceMap.get(name); if (instance != null) { return instance; } final Xnio newInstance = createConfigured(name); instanceMap.put(name, newInstance); return newInstance; } } /** * Get the {@code "default"} instance. Equivalent to calling {@link #getInstance(String) getInstance("default")}. * * @return the default instance * @throws IOException if the XNIO provider could not be created */ public static Xnio getInstance() throws IOException { return getInstance("default"); } private static Xnio createConfigured(final String name) throws IOException { try { return AccessController.doPrivileged(new PrivilegedAction() { public Xnio run() { final String fileName = System.getProperty(PROPERTY_FILE_PROPNAME, PROPERTIES); final Properties props = new Properties(); try { final InputStream stream = getClass().getResourceAsStream(fileName); if (stream != null) try { final InputStreamReader reader = new InputStreamReader(stream, "utf-8"); try { props.load(reader); reader.close(); } finally { IoUtils.safeClose(reader); } } finally { IoUtils.safeClose(stream); } } catch (IOException e) { throw new RuntimeException(e); } final XnioConfiguration conf = new XnioConfiguration(); if (Boolean.parseBoolean(props.getProperty(name + ".listener.threadpool", "false"))) { conf.setExecutor( new ThreadPoolExecutor( Integer.parseInt(props.getProperty(name + ".listener.threadpool.coresize", "8")), Integer.parseInt(props.getProperty(name + ".listener.threadpool.maxsize", "64")), Long.parseLong(props.getProperty(name + ".listener.threadpool.keepaliveseconds", "30")), TimeUnit.SECONDS, new ArrayBlockingQueue(Integer.parseInt(props.getProperty(name + ".listener.threadpool.queuelength", "64"))), new ThreadPoolExecutor.CallerRunsPolicy() ) ); } conf.setName(name); conf.setOptionMap(OptionMap.builder().parseAll(props, name + ".provider.option.").getMap()); try { return Xnio.create(props.getProperty(name + ".provider", PROVIDER_NAME), conf); } catch (IOException e) { throw new RuntimeException(e); } } }); } catch (RuntimeException e) { final Throwable c = e.getCause(); if (c instanceof IOException) { throw (IOException)c; } throw e; } } /** * Create an instance of the default XNIO provider. The provider name can be specified through the * {@code xnio.provider.name} system property. Any failure to create the XNIO provider will cause an {@code java.io.IOException} * to be thrown. * * @return an XNIO instance * @throws IOException if the XNIO provider could not be created */ public static Xnio create() throws IOException { return create(PROVIDER_NAME, new XnioConfiguration()); } /** * Create an instance of the default XNIO provider. The provider name can be specified through the * {@code xnio.provider.name} system property. Any failure to create the XNIO provider will cause an {@code java.io.IOException} * to be thrown. * * @param configuration the configuration parameters for the implementation * @return an XNIO instance * @throws IOException if the XNIO provider could not be created */ public static Xnio create(XnioConfiguration configuration) throws IOException { return create(PROVIDER_NAME, configuration); } /** * Create an instance of the named XNIO provider. Any failure to create the XNIO provider will cause an {@code java.io.IOException} * to be thrown. * * @param implName the name of the implementation * @param configuration the configuration parameters for the implementation * @return an XNIO instance * @throws IOException if the XNIO provider could not be created */ public static Xnio create(String implName, XnioConfiguration configuration) throws IOException { for (XnioProvider xnioProvider : ServiceLoader.load(XnioProvider.class)) { if (implName.equals(xnioProvider.getName())) { return xnioProvider.getNewInstance(configuration); } } throw new IOException("No XNIO provider named \"" + implName + "\" could be found"); } private static final AtomicInteger xnioSequence = new AtomicInteger(1); /** * Construct an XNIO provider instance. */ protected Xnio(XnioConfiguration configuration) { if (configuration == null) { throw new NullPointerException("configuration is null"); } final SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(SUBCLASS_PERMISSION); } final String name = configuration.getName(); final int seq = xnioSequence.getAndIncrement(); this.name = name != null ? name : String.format("%s-%d", getClass().getName(), Integer.valueOf(seq)); final Executor executor = configuration.getExecutor(); this.executor = executor != null ? executor : IoUtils.directExecutor(); // Perform MBeanServer autodetection... final List servers = mBeanServers; synchronized (servers) { final List confServers = configuration.getMBeanServers(); if (confServers != null) { for (MBeanServer server : confServers) { if (server == null) { throw new NullPointerException("server in MBeanServer configuration list is null"); } mlog.debug("Registered configured MBeanServer %s", server); servers.add(server); } } else { final String agentidpropval; try { agentidpropval = sm != null ? AccessController.doPrivileged(GET_AGENTID_ACTION) : System.getProperty(AGENTID_PROPNAME); } catch (SecurityException e) { // not allowed; leave mbean servers empty mlog.debug("Unable to read agentid property (%s); JMX features disabled", e); return; } if (agentidpropval == null || agentidpropval.length() == 0) { final Collection fullList; try { fullList = sm != null ? AccessController.doPrivileged(new GetMBeanServersAction(null)) : MBeanServerFactory.findMBeanServer(null); } catch (SecurityException e) { mlog.debug("Unable to detect installed mbean servers (%s); JMX features disabled", e); return; } for (MBeanServer match : fullList) { mlog.debug("Registered MBeanServer %s", match); servers.add(match); } } else { String[] agentids = agentidpropval.split(","); for (String agentid : agentids) { String properName = agentid.trim(); if (properName.length() == 0) { continue; } Collection matches; try { matches = sm != null ? AccessController.doPrivileged(new GetMBeanServersAction(properName)) : MBeanServerFactory.findMBeanServer(null); } catch (SecurityException e) { mlog.debug("Unable to locate any MBeanServer for ID \"%s\" (%s); skipping", properName, e); continue; } if (matches == null) { mlog.debug("Unable to locate any MBeanServer for ID \"%s\" (no matches); skipping", properName); } else { for (MBeanServer match : matches) { mlog.debug("Registered MBeanServer %s for ID \"%s\"", match, properName); servers.add(match); } } } } } } } /** * Create a managed socket factory which uses this provider's MBean configuration to track management information. * * @param optionMap the option map * @return the managed socket factory * * @since 2.0 */ public SocketFactory createManagedSocketFactory(OptionMap optionMap) { return new ManagedSocketFactory(this, optionMap); } /** * Create a managed server socket factory which uses this provider's MBean configuration to track management information. * * @param optionMap the option map * @return the managed server socket factory * * @since 2.0 */ public ServerSocketFactory createServerSocketFactory(OptionMap optionMap) { return new ManagedServerSocketFactory(this, optionMap); } /** * Create an unbound TCP server. The given executor will be used to execute listener methods. * * @param executor the executor to use to execute the listeners * @param openListener the initial open-connection listener * @param optionMap the initial configuration for the server * @return the unbound TCP server * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public TcpServer createTcpServer(Executor executor, ChannelListener openListener, OptionMap optionMap) { throw new UnsupportedOperationException("TCP Server"); } /** * Create an unbound TCP server. The provider's default executor will be used to execute listener methods. * * @param openListener the initial open-connection listener * @param optionMap the initial configuration for the server * @return the unbound TCP server * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public TcpServer createTcpServer(ChannelListener openListener, OptionMap optionMap) { return createTcpServer(executor, openListener, optionMap); } /** * Create an unbound TCP SSL server. The given executor will be used to execute listener methods and SSL tasks. * * @param executor the executor to use to execute the listeners * @param openListener the initial open-connection listener * @param optionMap the initial configuration for the server * @return the unbound TCP SSL server * @throws NoSuchProviderException if an SSL provider was selected which is not supported * @throws NoSuchAlgorithmException if an SSL algorithm was selected which is not supported * * @since 2.1 */ public SslTcpServer createSslTcpServer(Executor executor, ChannelListener openListener, OptionMap optionMap) throws NoSuchProviderException, NoSuchAlgorithmException { final SSLContext sslContext = getSSLContext(optionMap); final SslEngineSslTcpServer server = new SslEngineSslTcpServer(sslContext, createTcpServer(executor, null, optionMap), executor, optionMap); if (openListener != null) server.getOpenSetter().set(openListener); return server; } private SSLContext getSSLContext(final OptionMap optionMap) throws NoSuchAlgorithmException, NoSuchProviderException { final String provider = optionMap.get(Options.SSL_PROVIDER); final String protocol = optionMap.get(Options.SSL_PROTOCOL); final SSLContext sslContext; if (protocol == null) { sslContext = SSLContext.getDefault(); } else if (provider == null) { sslContext = SSLContext.getInstance(protocol); } else { sslContext = SSLContext.getInstance(protocol, provider); } return sslContext; } /** * Create an unbound TCP SSL server. The provider's default executor will be used to execute listener methods and SSL tasks. * * @param openListener the initial open-connection listener * @param optionMap the initial configuration for the server * @return the unbound TCP SSL server * @throws NoSuchProviderException if an SSL provider was selected which is not supported * @throws NoSuchAlgorithmException if an SSL algorithm was selected which is not supported * * @since 2.1 */ public SslTcpServer createSslTcpServer(ChannelListener openListener, OptionMap optionMap) throws NoSuchProviderException, NoSuchAlgorithmException { return createSslTcpServer(executor, openListener, optionMap); } /** * Create an unbound TCP SSL server. The provider's default executor will be used to execute listener methods and SSL tasks. * * @param optionMap the initial configuration for the server * @return the unbound TCP SSL server * @throws NoSuchProviderException if an SSL provider was selected which is not supported * @throws NoSuchAlgorithmException if an SSL algorithm was selected which is not supported * * @since 2.1 */ public SslTcpServer createSslTcpServer(OptionMap optionMap) throws NoSuchProviderException, NoSuchAlgorithmException { return createSslTcpServer(null, optionMap); } /** * Create a TCP connector. The given executor will be used to execute listener methods. * * @param executor the executor to use to execute the listeners * @param optionMap the initial configuration for the connector * @return the TCP connector * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public TcpConnector createTcpConnector(Executor executor, OptionMap optionMap) { return createTcpConnector(executor, null, optionMap); } /** * Create a TCP connector. The provider's default executor will be used to execute listener methods. * * @param optionMap the initial configuration for the connector * @return the TCP connector * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public TcpConnector createTcpConnector(OptionMap optionMap) { return createTcpConnector(executor, optionMap); } /** * Create a TCP connector. The given executor will be used to execute listener methods. * * @param executor the executor to use to execute the listeners * @param src the source address for connections * @param optionMap the initial configuration for the connector * @return the TCP connector * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public TcpConnector createTcpConnector(Executor executor, InetSocketAddress src, OptionMap optionMap) { throw new UnsupportedOperationException("TCP Connector"); } /** * Create a TCP connector. The provider's default executor will be used to execute listener methods. * * @param src the source address for connections * @param optionMap the initial configuration for the connector * @return the TCP connector * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public TcpConnector createTcpConnector(InetSocketAddress src, OptionMap optionMap) { return createTcpConnector(executor, src, optionMap); } /** * Create an SSL TCP connector. The given executor will be used to execute listener methods. * * @param executor the executor to use to execute the listeners * @param src the source address for connections * @param optionMap the initial configuration for the connector * @return the SSL TCP connector * @throws NoSuchProviderException if an SSL provider was selected which is not supported * @throws NoSuchAlgorithmException if an SSL algorithm was selected which is not supported * * @since 2.1 */ public SslTcpConnector createSslTcpConnector(final Executor executor, InetSocketAddress src, final OptionMap optionMap) throws NoSuchProviderException, NoSuchAlgorithmException { final SSLContext sslContext = getSSLContext(optionMap); final TcpConnector connector = createTcpConnector(executor, src, optionMap); return new SslTcpConnector() { @SuppressWarnings({ "deprecation" }) public IoFuture connectTo(final InetSocketAddress destination, final ChannelListener openListener, final ChannelListener> bindListener) { final FutureResult futureResult = new FutureResult(executor); connector.connectTo(destination, new ChannelListener() { public void handleEvent(final TcpChannel tcpChannel) { final SslTcpChannel channel = Channels.createSslTcpChannel(sslContext, tcpChannel, executor, optionMap); futureResult.setResult(channel); IoUtils.invokeChannelListener(channel, openListener); } }, bindListener).addNotifier( new IoFuture.HandlingNotifier>() { public void handleCancelled(final FutureResult result) { result.setCancelled(); } public void handleFailed(final IOException exception, final FutureResult result) { result.setException(exception); } }, futureResult); return futureResult.getIoFuture(); } public ChannelSource createChannelSource(final InetSocketAddress destination) { return new ChannelSource() { public IoFuture open(final ChannelListener openListener) { return connectTo(destination, openListener, null); } }; } }; } /** * Create an SSL TCP connector. The provider's default executor will be used to execute listener methods. * * @param src the source address for connections * @param optionMap the initial configuration for the connector * @return the SSL TCP connector * @throws NoSuchProviderException if an SSL provider was selected which is not supported * @throws NoSuchAlgorithmException if an SSL algorithm was selected which is not supported * * @since 2.1 */ public SslTcpConnector createSslTcpConnector(InetSocketAddress src, final OptionMap optionMap) throws NoSuchProviderException, NoSuchAlgorithmException { return createSslTcpConnector(executor, src, optionMap); } /** * Create an SSL TCP connector. The provider's default executor will be used to execute listener methods. * * @param optionMap the initial configuration for the connector * @return the SSL TCP connector * @throws NoSuchProviderException if an SSL provider was selected which is not supported * @throws NoSuchAlgorithmException if an SSL algorithm was selected which is not supported * * @since 2.1 */ public SslTcpConnector createSslTcpConnector(final OptionMap optionMap) throws NoSuchProviderException, NoSuchAlgorithmException { return createSslTcpConnector(executor, null, optionMap); } /** * Create an unbound UDP server. The UDP server can be configured to be multicast-capable; this should only be * done if multicast is needed, since some providers have a performance penalty associated with multicast. * The given executor will be used to execute listener methods. * * @param executor the executor to use to execute the listeners * @param bindListener the initial open-connection listener * @param optionMap the initial configuration for the server * * @return a factory that can be used to configure the new UDP server * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public UdpServer createUdpServer(Executor executor, ChannelListener bindListener, OptionMap optionMap) { throw new UnsupportedOperationException("UDP Server"); } /** * Create an unbound UDP server. The UDP server can be configured to be multicast-capable; this should only be * done if multicast is needed, since some providers have a performance penalty associated with multicast. * The provider's default executor will be used to execute listener methods. * * @param bindListener the initial open-connection listener * @param optionMap the initial configuration for the server * * @return a factory that can be used to configure the new UDP server * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public UdpServer createUdpServer(ChannelListener bindListener, OptionMap optionMap) { return createUdpServer(executor, bindListener, optionMap); } /** * Create an unbound UDP server. The UDP server can be configured to be multicast-capable; this should only be * done if multicast is needed, since some providers have a performance penalty associated with multicast. * The provider's default executor will be used to execute listener methods. * * @param optionMap the initial configuration for the server * * @return a factory that can be used to configure the new UDP server * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public UdpServer createUdpServer(OptionMap optionMap) { return createUdpServer(executor, IoUtils.nullChannelListener(), optionMap); } /** * Create a pipe "server". The provided open listener acts upon the server "end" of the * pipe. The returned channel source is used to establish connections to the server. * * @param executor the executor to use to execute the listeners * @param openListener the initial open-connection listener * * @return the client channel source * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public ChannelSource createPipeServer(Executor executor, ChannelListener openListener) { throw new UnsupportedOperationException("Pipe Server"); } /** * Create a pipe "server". The provided open listener acts upon the server "end" of the * pipe. The returned channel source is used to establish connections to the server. The provider's default executor will be used to * execute listener methods. * * @param openListener the initial open-connection listener * * @return the client channel source * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public ChannelSource createPipeServer(ChannelListener openListener) { return createPipeServer(executor, openListener); } /** * Create a one-way pipe "server". The provided open listener acts upon the server "end" of the * the pipe. The returned channel source is used to establish connections to the server. The data flows from the * server to the client. * * @param executor the executor to use to execute the listeners * @param openListener the initial open-connection listener * * @return the client channel source * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public ChannelSource createPipeSourceServer(Executor executor, ChannelListener openListener) { throw new UnsupportedOperationException("One-way Pipe Server"); } /** * Create a one-way pipe "server". The provided open listener acts upon the server "end" of the * the pipe. The returned channel source is used to establish connections to the server. The data flows from the * server to the client. The provider's default executor will be used to * execute listener methods. * * @param openListener the initial open-connection listener * * @return the client channel source * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public ChannelSource createPipeSourceServer(ChannelListener openListener) { return createPipeSourceServer(executor, openListener); } /** * Create a one-way pipe "server". The provided open listener acts upon the server "end" of the * the pipe. The returned channel source is used to establish connections to the server. The data flows from the * client to the server. * * @param executor the executor to use to execute the listeners * @param openListener the initial open-connection listener * * @return the client channel source * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public ChannelSource createPipeSinkServer(Executor executor, ChannelListener openListener) { throw new UnsupportedOperationException("One-way Pipe Server"); } /** * Create a one-way pipe "server". The provided open listener acts upon the server "end" of the * the pipe. The returned channel source is used to establish connections to the server. The data flows from the * client to the server. The provider's default executor will be used to * execute listener methods. * * @param openListener the initial open-connection listener * * @return the client channel source * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public ChannelSource createPipeSinkServer(ChannelListener openListener) { return createPipeSinkServer(executor, openListener); } /** * Create a single pipe connection. * * @param executor the executor to use to execute the listeners * @param leftListener the open listener for the "left" side of the pipe * @param rightListener the open listener for the "right" side of the pipe * * @return the future connection * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public IoFuture createPipeConnection(Executor executor, ChannelListener leftListener, ChannelListener rightListener) { throw new UnsupportedOperationException("Pipe Connection"); } /** * Create a single pipe connection. The provider's default executor will be used to * execute listener methods. * * @param leftListener the listener for the "left" side of the pipe * @param rightListener the listener for the "right" side of the pipe * * @return the future connection * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public IoFuture createPipeConnection(ChannelListener leftListener, ChannelListener rightListener) { return createPipeConnection(executor, leftListener, rightListener); } /** * Create a single one-way pipe connection. * * @param executor the executor to use to execute the listeners * @param sourceListener the listener for the "source" side of the pipe * @param sinkListener the listener for the "sink" side of the pipe * * @return the future connection * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public IoFuture createOneWayPipeConnection(Executor executor, ChannelListener sourceListener, ChannelListener sinkListener) { throw new UnsupportedOperationException("One-way Pipe Connection"); } /** * Create a single one-way pipe connection. The provider's default executor will be used to * execute listener methods. * * @param sourceListener the listener for the "source" side of the pipe * @param sinkListener the listener for the "sink" side of the pipe * * @return the future connection * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public IoFuture createOneWayPipeConnection(ChannelListener sourceListener, ChannelListener sinkListener) { return createOneWayPipeConnection(executor, sourceListener, sinkListener); } /** * Create a TCP acceptor. * * @param executor the executor to use to execute the listeners * @param optionMap the initial configuration for the acceptor * @return the TCP acceptor * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public TcpAcceptor createTcpAcceptor(Executor executor, OptionMap optionMap) { throw new UnsupportedOperationException("TCP Acceptor"); } /** * Create a TCP acceptor. * * @param optionMap the initial configuration for the acceptor * @return the TCP acceptor * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public TcpAcceptor createTcpAcceptor(OptionMap optionMap) { return createTcpAcceptor(executor, optionMap); } /** * Create a local stream server. The stream server can be bound to one or more files in the filesystem. * * @param executor the executor to use to execute the listeners * @param openListener a listener which is notified on channel open * @param optionMap the initial configuration for the server * * @return a factory that can be used to configure the new stream server * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public LocalServer createLocalStreamServer(Executor executor, ChannelListener> openListener, OptionMap optionMap) { throw new UnsupportedOperationException("Local IPC Stream Server"); } /** * Create a local stream server. The stream server can be bound to one or more files in the filesystem. * * @param openListener a listener which is notified on channel open * @param optionMap the initial configuration for the server * * @return a factory that can be used to configure the new stream server * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public LocalServer createLocalStreamServer(ChannelListener> openListener, OptionMap optionMap) { return createLocalStreamServer(executor, openListener, optionMap); } /** * Create a local stream connector. * * @param executor the executor to use to execute the listeners * @param optionMap the initial configuration for the connector * * @return the stream connector * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public LocalStreamConnector createLocalStreamConnector(Executor executor, OptionMap optionMap) { throw new UnsupportedOperationException("Local IPC Stream Connector"); } /** * Create a local stream connector. * * @param optionMap the initial configuration for the connector * * @return the stream connector * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public LocalStreamConnector createLocalStreamConnector(OptionMap optionMap) { return createLocalStreamConnector(executor, optionMap); } /** * Create a local datagram server. The datagram server is bound to one or more files in the filesystem. * * @param executor the executor to use to execute the listeners * @param openListener the initial open-connection listener * @param optionMap the initial configuration for the server * * @return the new datagram server * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public LocalServer createLocalDatagramServer(Executor executor, ChannelListener> openListener, OptionMap optionMap) { throw new UnsupportedOperationException("Local IPC Datagram Server"); } /** * Create a local datagram server. The datagram server is bound to one or more files in the filesystem. * * @param openListener the initial open-connection listener * @param optionMap the initial configuration for the server * * @return the new datagram server * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public LocalServer createLocalDatagramServer(ChannelListener> openListener, OptionMap optionMap) { return createLocalDatagramServer(executor, openListener, optionMap); } /** * Create a local datagram connector. * * @param executor the executor to use to execute the listeners * @param optionMap the initial configuration for the connector * @return the new datagram connector * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public LocalDatagramConnector createLocalDatagramConnector(Executor executor, OptionMap optionMap) { throw new UnsupportedOperationException("Local IPC Datagram Connector"); } /** * Create a local datagram connector. * * @param optionMap the initial configuration for the connector * @return the new datagram connector * * @since 2.0 */ @SuppressWarnings({ "UnusedDeclaration" }) public LocalDatagramConnector createLocalDatagramConnector(OptionMap optionMap) { return createLocalDatagramConnector(executor, optionMap); } /** * Wake up any blocking I/O operation being carried out on a given thread. Custom implementors of {@link Thread} * may call this method from their implementation of {@link Thread#interrupt()} after the default implementation * to ensure that any thread waiting in a blocking operation is woken up in a timely manner. Some implementations * may not implement this method, relying instead on the interruption mechanism built in to the JVM; as such this * method should not be relied upon as a guaranteed way to awaken a blocking thread independently of thread * interruption. * * @param targetThread the thread to awaken * * @since 1.2 */ @SuppressWarnings({ "UnusedDeclaration" }) public void awaken(Thread targetThread) { // nothing by default } /** * Get the name of this XNIO instance. * * @return the name */ public String getName() { return name; } /** * Get a string representation of this XNIO instance. * * @return the string representation */ public String toString() { return String.format("XNIO provider \"%s\" <%s@%s>", getName(), getClass().getName(), Integer.toHexString(hashCode())); } /** * Close this XNIO provider. Calling this method more than one time has no additional effect. */ public abstract void close() throws IOException; private Closeable registerMBean(final Object mBean, final ObjectName mBeanName) { final SecurityManager sm = System.getSecurityManager(); final List servers = mBeanServers; synchronized (servers) { final Iterator it = servers.iterator(); if (!it.hasNext()) { return IoUtils.nullCloseable(); } else { final List registrations = new ArrayList(servers.size()); do { final MBeanServer server = it.next(); if (sm != null) { AccessController.doPrivileged(new PrivilegedAction() { public Void run() { doRegister(mBean, mBeanName, registrations, server); return null; } }); } else { doRegister(mBean, mBeanName, registrations, server); } } while (it.hasNext()); return new RegHandle(registrations); } } } private static void doRegister(final Object mBean, final ObjectName mBeanName, final List registrations, final MBeanServer server) { try { final ObjectInstance instance = server.registerMBean(mBean, mBeanName); registrations.add(new Registration(server, instance.getObjectName())); } catch (JMException e) { mlog.debug(e, "Failed to register mBean named \"%s\" on server %s", mBeanName, server); } catch (RuntimeOperationsException e) { mlog.debug(e, "Failed to register mBean named \"%s\" on server %s", mBeanName, server); } } private interface Entry extends Map.Entry {} private static Entry entry(final String k, final String v) { return new Entry() { public String getKey() { return k; } public String getValue() { return v; } public String setValue(final String value) { throw new UnsupportedOperationException("setValue"); } }; } private static Hashtable hashtable(Entry... entries) { final Hashtable table = new Hashtable(entries.length); for (Entry entry : entries) { table.put(entry.getKey(), entry.getValue()); } return table; } /** * Get an XNIO property. The property name must start with {@code "xnio."}. * * @param name the property name * @return the property value, or {@code null} if it wasn't found * @since 1.2 */ protected String getProperty(final String name) { if (! name.startsWith("xnio.")) { throw new SecurityException("Not allowed to read non-XNIO properties"); } final SecurityManager sm = System.getSecurityManager(); if (sm != null) { return AccessController.doPrivileged(new GetPropertyAction(name, null)); } else { return System.getProperty(name); } } /** * Get an XNIO property. The property name must start with {@code "xnio."}. * * @param name the property name * @param defaultValue the default value * @return the property value, or {@code defaultValue} if it wasn't found * @since 1.2 */ protected String getProperty(final String name, final String defaultValue) { if (! name.startsWith("xnio.")) { throw new SecurityException("Not allowed to read non-XNIO properties"); } final SecurityManager sm = System.getSecurityManager(); if (sm != null) { return AccessController.doPrivileged(new GetPropertyAction(name, defaultValue)); } else { return System.getProperty(name, defaultValue); } } /** * Register a TCP server MBean. * * @param mBean the MBean * @return a handle which may be used to unregister the MBean * @since 1.2 */ protected Closeable registerMBean(final TcpServerMBean mBean) { try { final ObjectName mbeanName = new ObjectName(MANAGEMENT_DOMAIN, hashtable( entry("provider", ObjectName.quote(getName())), entry("type", "server"), entry("protocol", "tcp"), entry("id", Long.toString(mbeanSequence.getAndIncrement())) /* TODO: name? */ )); return registerMBean(mBean, mbeanName); } catch (MalformedObjectNameException e) { throw new IllegalStateException("Unexpected exception", e); } } /** * Register a TCP connection MBean. * * @param mBean the MBean * @return a handle which may be used to unregister the MBean * @since 1.2 */ protected Closeable registerMBean(final TcpConnectionMBean mBean) { try { final ObjectName mbeanName = new ObjectName(MANAGEMENT_DOMAIN, hashtable( entry("provider", ObjectName.quote(getName())), entry("type", "connection"), entry("protocol", "tcp"), entry("bindAddress", ObjectName.quote(mBean.getBindAddress().toString())), entry("peerAddress", ObjectName.quote(mBean.getPeerAddress().toString())), entry("id", Long.toString(mbeanSequence.getAndIncrement())) )); return registerMBean(mBean, mbeanName); } catch (MalformedObjectNameException e) { throw new IllegalStateException("Unexpected exception", e); } } /** * Register a UDP server MBean. * * @param mBean the MBean * @return a handle which may be used to unregister the MBean * @since 1.2 */ protected Closeable registerMBean(final UdpServerMBean mBean) { try { final ObjectName mbeanName = new ObjectName(MANAGEMENT_DOMAIN, hashtable( entry("provider", ObjectName.quote(getName())), entry("type", "server"), entry("protocol", "udp"), entry("id", Long.toString(mbeanSequence.getAndIncrement())) )); return registerMBean(mBean, mbeanName); } catch (MalformedObjectNameException e) { throw new IllegalStateException("Unexpected exception", e); } } /** * Register a one-way pipe connection MBean. * * @param mBean the MBean * @return a handle which may be used to unregister the MBean * @since 1.2 */ protected Closeable registerMBean(final OneWayPipeConnectionMBean mBean) { try { final ObjectName mbeanName = new ObjectName(MANAGEMENT_DOMAIN, hashtable( entry("provider", ObjectName.quote(getName())), entry("type", "connection"), entry("protocol", "local"), entry("id", Long.toString(mbeanSequence.getAndIncrement())) )); return registerMBean(mBean, mbeanName); } catch (MalformedObjectNameException e) { throw new IllegalStateException("Unexpected exception", e); } } /** * Register a pipe connection MBean. * * @param mBean the MBean * @return a handle which may be used to unregister the MBean * @since 1.2 */ protected Closeable registerMBean(final PipeConnectionMBean mBean) { try { final ObjectName mbeanName = new ObjectName(MANAGEMENT_DOMAIN, hashtable( entry("provider", ObjectName.quote(getName())), entry("type", "connection"), entry("protocol", "local"), entry("id", Long.toString(mbeanSequence.getAndIncrement())) )); return registerMBean(mBean, mbeanName); } catch (MalformedObjectNameException e) { throw new IllegalStateException("Unexpected exception", e); } } /** * Register a pipe server MBean. * * @param mBean the MBean * @return a handle which may be used to unregister the MBean * @since 1.2 */ protected Closeable registerMBean(final PipeServerMBean mBean) { try { final ObjectName mbeanName = new ObjectName(MANAGEMENT_DOMAIN, hashtable( entry("provider", ObjectName.quote(getName())), entry("type", "server"), entry("protocol", "local"), entry("id", Long.toString(mbeanSequence.getAndIncrement())) )); return registerMBean(mBean, mbeanName); } catch (MalformedObjectNameException e) { throw new IllegalStateException("Unexpected exception", e); } } /** * Register a pipe source server MBean. * * @param mBean the MBean * @return a handle which may be used to unregister the MBean * @since 1.2 */ protected Closeable registerMBean(final PipeSourceServerMBean mBean) { try { final ObjectName mbeanName = new ObjectName(MANAGEMENT_DOMAIN, hashtable( entry("provider", ObjectName.quote(getName())), entry("type", "server"), entry("protocol", "local-source"), entry("id", Long.toString(mbeanSequence.getAndIncrement())) )); return registerMBean(mBean, mbeanName); } catch (MalformedObjectNameException e) { throw new IllegalStateException("Unexpected exception", e); } } /** * Register a pipe sink server MBean. * * @param mBean the MBean * @return a handle which may be used to unregister the MBean * @since 1.2 */ protected Closeable registerMBean(final PipeSinkServerMBean mBean) { try { final ObjectName mbeanName = new ObjectName(MANAGEMENT_DOMAIN, hashtable( entry("provider", ObjectName.quote(getName())), entry("type", "server"), entry("protocol", "local-sink"), entry("id", Long.toString(mbeanSequence.getAndIncrement())) )); return registerMBean(mBean, mbeanName); } catch (MalformedObjectNameException e) { throw new IllegalStateException("Unexpected exception", e); } } private static final class Registration { private final MBeanServer server; private final ObjectName objectName; private Registration(final MBeanServer server, final ObjectName objectName) { this.server = server; this.objectName = objectName; } } private static final class RegHandle implements Closeable { private final List registrations; private final AtomicBoolean open = new AtomicBoolean(true); private RegHandle(final List registrations) { this.registrations = registrations; } public void close() throws IOException { if (open.getAndSet(false)) { final SecurityManager sm = System.getSecurityManager(); for (final Registration registration : registrations) { if (sm != null) { AccessController.doPrivileged(new PrivilegedAction() { public Void run() { doUnregister(registration); return null; } }); } else { doUnregister(registration); } } } } private static void doUnregister(final Registration registration) { final MBeanServer server = registration.server; final ObjectName mBeanName = registration.objectName; try { server.unregisterMBean(mBeanName); } catch (InstanceNotFoundException e) { mlog.debug(e, "Failed to unregister mBean named \"%s\" on server %s", mBeanName, server); } catch (MBeanRegistrationException e) { mlog.debug(e, "Failed to unregister mBean named \"%s\" on server %s", mBeanName, server); } } } private static final class GetMBeanServersAction implements PrivilegedAction> { private final String agentId; public GetMBeanServersAction(final String agentId) { this.agentId = agentId; } public Collection run() { return MBeanServerFactory.findMBeanServer(agentId); } } private static final class GetPropertyAction implements PrivilegedAction { private final String propertyName; private final String defaultValue; private GetPropertyAction(final String propertyName, final String defaultValue) { this.propertyName = propertyName; this.defaultValue = defaultValue; } public String run() { return System.getProperty(propertyName, defaultValue); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy