Chapter 4. Configuration

This covers the configuration for JBoss Remoting discovery, connectors, marshallers, and transports. All the configuration properties specified can be set either via calls to the object itself, including via JMX (so can be done via the JMX or Web console), or via a JBoss AS service xml file. Examples of service xml configurations can be seen with each of the sections below. There is also an example-service.xml file included in the remoting distribution that shows full examples of all the remoting configurations.

4.1. General Connector and Invoker configuration

The server invoker and invocation handlers are configured via the Connector. Only one invoker can be declared per connector (multiple InvokerLocator attributes or invoker elements within the Configuration attribute is not permitted). Although declaring an invocation handler is not required, it should only be omitted in the case of declaring a callback server that will not receive direct invocations, but only callback messages. Otherwise client invocations can not be processed. The invocation handler is the only interface that is required by the remoting framework for a user to implement and will be what the remoting framework calls upon when receiving invocations.

There are two ways in which to specify the server invoker configuration via a service xml file. The first is to specify just the InvokerLocator attribute as a sub-element of the Connector MBean. For example, a possible configuration for a Connector using a socket invoker that is listening on port 8084 on the test.somedomain.com address would be:

         <mbean code="org.jboss.remoting.transport.Connector"
               xmbean-dd="org/jboss/remoting/transport/Connector.xml"
               name="jboss.remoting:service=Connector,transport=Socket"
               display-name="Socket transport Connector">
            <attribute name="InvokerLocator">
               <![CDATA[socket://test.somedomain.com:8084]]>
            </attribute>
            <attribute name="Configuration">
               <config>
                  <handlers>
                     <handler subsystem="mock">
                        org.jboss.remoting.transport.mock.MockServerInvocationHandler
                     </handler>
                  </handlers>
               </config>
            </attribute>
         </mbean>
      

Note that all the server side socket invoker configurations will be set to their default values in this case. Also, it is important to add CDATA to any locator uri that contains more than one parameter.

The other way to configure the Connector and its server invoker in greater detail is to provide an invoker sub-element within the config element of the Configuration attribute. The only attribute of invoker element is transport, which will specify which transport type to use (e.g.. socket, rmi, http, or multiplex). All the sub-elements of the invoker element will be attribute elements with a name attribute specifying the configuration property name and then the value. An isParam attribute can also be added to indicate that the attribute should be added to the locator uri, in the case the attribute needs to be used by the client. An example using this form of configuration is as follows:

 
         <mbean code="org.jboss.remoting.transport.Connector"
               xmbean-dd="org/jboss/remoting/transport/Connector.xml"
               name="jboss.remoting:service=Connector,transport=Socket"
               display-name="Socket transport Connector">

            <attribute name="Configuration">
               <config>
                
                  <invoker transport="socket">
                     <attribute name="numAcceptThreads">1</attribute>
                     <attribute name="maxPoolSize">303</attribute>
                     <attribute name="clientMaxPoolSize" isParam="true">304</attribute>
                     <attribute name="socketTimeout">60000</attribute>
                     <attribute name="serverBindAddress">192.168.0.82</attribute>
                     <attribute name="serverBindPort">6666</attribute>
                     <attribute name="clientConnectAddress">216.23.33.2</attribute>
                     <attribute name="clientConnectPort">7777</attribute>
                     <attribute name="enableTcpNoDelay" isParam="true">false</attribute>
                     <attribute name="backlog">200</attribute>
                  </invoker>
               
                  <handlers>
                     <handler subsystem="mock">
                        org.jboss.remoting.transport.mock.MockServerInvocationHandler
                     </handler>
                  </handlers>
               </config>
            </attribute>

         </mbean>
      

Also note that ${jboss.bind.address} can be used for any of the bind address properties, which will be replaced with the bind address specified to JBoss when starting (i.e. via the -b option).

All the attributes set in this configuration could be set directly in the locator uri of the InvokerLocator attribute value, but would be much more difficult to decipher visually and is more prone to editing mistakes.

One of the components of a locator uri that can be expressed within the InvokerLocator attribute is the path. For example, can express a locator uri path of 'foo/bar' via the InvokerLocator attribute as:

            <attribute name="InvokerLocator"><![CDATA[socket://test.somedomain.com:8084/foo/bar]]></attribute>

To include the path using the Configuration attribute, can include a specific 'path' attribute. So the same InvokerLocator can be expressed as follows with the Configuration attribute:

            <attribute name="Configuration">
               <config>
                 <invoker transport="socket">
                   <attribute name="serverBindAddress">test.somedomain.com</attribute>
                   <attribute name="serverBindPort">8084</attribute>
                   <attribute name="path">foo/bar</attribute>
                 </invoker>
                 ...

Note: The value for the 'path' attribute should NOT start or end with a / (slash).

4.2. Handlers

Handlers are classes that the invocation is given to on the server side (the final target for remoting invocations). To implement a handler, all that is needed is to implement the org.jboss.remoting.ServerInvocationHandler interface. There are a two ways in which to register a handler with a Connector. The first is to do it programmatically. The second is via service configuration. For registering programmatically, can either pass the ServerInvocationHandler reference itself or an ObjectName for the ServerInvocationHandler (in the case that it is an MBean). To pass the handler reference directly, call Connector::addInvocationHandler(String subsystem, ServerInvocationHandler handler). For example (from org.jboss.remoting.samples.simple.SimpleServer):

            InvokerLocator locator = new InvokerLocator(locatorURI);
            Connector connector = new Connector();
            connector.setInvokerLocator(locator.getLocatorURI());
            connector.create();

            SampleInvocationHandler invocationHandler = new SampleInvocationHandler();
            // first parameter is sub-system name. can be any String value.
            connector.addInvocationHandler("sample", invocationHandler);

            connector.start();
         

To pass the handler by ObjectName, call Connector::addInvocationHandler(String subsystem, ObjectName handlerObjectName) . For example (from org.jboss.test.remoting.handler.mbean.ServerTest):

            MBeanServer server = MBeanServerFactory.createMBeanServer();
            InvokerLocator locator = new InvokerLocator(locatorURI);
            Connector connector = new Connector();
            connector.setInvokerLocator(locator.getLocatorURI());
            connector.start();

            server.registerMBean(connector, new ObjectName("test:type=connector,transport=socket"));

            // now create Mbean handler and register with mbean server
            MBeanHandler handler = new MBeanHandler();
            ObjectName objName = new ObjectName("test:type=handler");
            server.registerMBean(handler, objName);

            connector.addInvocationHandler("test", objName);
         

Is important to note that if not starting the Connector via the service configuration, will need to explicitly register it with the MBeanServer (will throw exception otherwise).

If using a service configuration for starting the Connector and registering handlers, can either specify the fully qualified class name for the handler, which will instantiate the handler instance upon startup (which requires there be a void parameter constructor), such as:

           
            <handlers>
              <handler subsystem="mock">
                org.jboss.remoting.transport.mock.MockServerInvocationHandler
              </handler>
            </handlers>
         

where MockServerInvocationHandler will be constructed upon startup and registered with the Connector as a handler.

Can also use an ObjectName to specify the handler. The configuration is the same, but instead of specifying a fully qualified class name, you specify the ObjectName for the handler, such as (can see mbeanhandler-service.xml under remoting tests for full example):

          
            <handlers>
              <handler subsystem="mock">test:type=handler</handler>
            </handlers>
         

The only requirement for this configuration is that the handler MBean must already be created and registered with the MBeanServer at the point the Connector is started.

Handler implementations

The Connectors will maintain a reference to the handler instances provided (either indirectly via the MBean proxy or directly via the instance object reference). For each request to the server invoker, the handler will be called upon. Since the server invokers can be multi-threaded (and in most cases would be), this means that the handler may receive concurrent calls to handle invocations. Therefore, handler implementations should take care to be thread safe in their implementations.

Stream handler

There is also an invocation handler interface that extends the ServerInvocationHandler interface specifically for handling of input streams as well as normal invocations. See the section on sending streams for further details. As for Connector configuration, it is the same.

HTTP handlers

Since there is extra information needed when dealing with the http transport, such as headers and response codes, special consideration is needed by handlers. The handlers receiving http invocations can get and set this extra information via the InvocationRequest that is passed to the handler.

Server invoker for the http transport will add the following to the InvocationRequest's request payload map:

MethodType - the http request type (i.e., GET, POST, PUT, HEADER, OPTIONS). Can use the contant value HTTPMetadataConstants.METHODTYPE, if don't want to use the actual string 'MethodType' as the key to the request payload map.

Path - the url path. Can use the contant value HTTPMetadataConstants.PATH, if don't want to use the actual string 'Path' as the key to the request payload map.

HttpVersion - the client's http version. Can use the contant value HTTPMetadataConstants.HTTPVERSION, if don't want to use the actual string 'HttpVersion' as the key to the request payload map.

Other properties from the original http request will also be included in the request payload map, such as request headers. Can reference org.jboss.test.remoting.transport.http.method.MethodInvocationHandler as an example for pulling request properties from the InvocationRequest.

The only time this will not be added is a POST request where an InvocationRequest is passed and is not binary content type (application/octet-stream).

The handlers receiving http invocations can also set the response code, response message, and response headers. To do this, will need to get the return payload map from the InvocationRequest passed (via its getReturnPayload() method). Then populate this map with whatever properties needed. For response code and message, will need to use the following keys for the map:

ResponseCode - Can use the constant value HTTPMetaDataConstants.RESPONSE_CODE, if don't want to use the actual string 'ResponseCode' as they key. IMPORTANT - The value put into map for this key MUST be of type java.lang.Integer.

ResponseCodeMessage - Can use the constant value HTTPMetadataConstants.RESPONSE_CODE_MESSAGE, if don't want to use the actual string 'ResponseCodeMessage' as the key. The value put into map for this key should be of type java.lang.String.

Is also important to note that ALL http requests will be passed to the handler. So even OPTIONS, HEAD, and PUT method requests will need to be handled. So, for example, if want to accept OPTIONS method requests, would need to populate response map with key of 'Allow' and value of 'OPTIONS, POST, GET, HEAD, PUT', in order to tell calling client that all these method types are allowed. Can see an example of how to do this within org.jboss.test.remoting.transport.http.method.MethodInvocationHandler.

The PUT request will be handled the same as a POST method request and the PUT request payload will be included within the InvocationRequest passed to the server handler. It is up to the server handler to set the proper resonse code (or throw proper exception) for the processing of the PUT request. See http://www.ietf.org/rfc/rfc2616.txt?number=2616 , section 9.6 for details on response codes and error responses).

HTTP Client

The HttpClientInvoker will now put the return from HTTPURLConnection's getHeaderFields() method into the metadata map passed to the Client's invoke() method (if not null). This means that if the caller passes a non-null Map, it can then get the response headers. It is important to note that each response header field key in the metadata map is associated with a list of response header values, so to get a value, would need code similar to:

Object response = remotingClient.invoke((Object) null, metadata); 
String allowValue = (String) ((List) metadata.get("Allow").get(0);

Can reference org.jboss.test.remoting.transport.http.method.HTTPInvokerTestClient for an example of this.

Note that when making a http request using the OPTIONS method type, the return from the Client's invoke() method will ALWAYS be null.

Also, if the response code is 400, the response returned will be that of the error stream and not the standard input stream. So is important to check for the response code.

Two values that will always be set within the metadata map passed to the Client's invoke() method (when not null), is the response code and response message from the server. These can be found using the keys:

ResponseCode - Can use the constant value HTTPMetaDataConstants.RESPONSE_CODE, if don't want to use the actual string 'ResponseCode' as the key. IMPORTANT - The value returned for this key will be of type java.lang.Integer.

ResponseCodeMessage - Can use the constant value from HTTPMetadataConstants.RESPONSE_CODE_MESSAGE, if don't want to use the actual string 'ResponseCodeMessage' as the key. The value returned for this key will be of type java.lang.String.

An example of getting the response code can be found within org.jboss.test.remoting.transport.http.method.HTTPInvokerTestClient.

4.3. Discovery (Detectors)

Domains

Detectors have the ability to accept multiple domains. What domains that the detector will accept as viewable can either be set programmatically via the method:

public void setConfiguration(org.w3c.dom.Element xml)

or by adding to jboss-service.xml configuration for the detector. The domains that the detector is currently accepting can be retrieved from the method:

public org.w3c.dom.Element getConfiguration()

The configuration xml is a MBean attribute of the detector, so can be set or retrieved via JMX.

There are three possible options for setting up the domains that a detector will accept. The first is to not call the setConfiguration() method (or just not add the configuration attribute to the service xml). This will cause the detector to use only its domain and is the default behavior. This enables it to be backwards compatible with earlier versions of JBoss Remoting (JBoss 4, DR2 and before).

The second is to call the setConfiguration() method (or add the configuration attribute to the service xml) with the following xml element:

    
          <domains>
            <domain>domain1</domain>
            <domain>domain2</domain>
          </domains>
         

where domain1 and domain2 are the two domains you would like the detector to accept. This will cause the detector to accept detections only from the domains specified, and no others.

The third and final option is to call the setConfiguration() method (or add the configuration attribute to the service xml) with the following xml element:

          <domains>
          </domains>
      

This will cause the detector to accept all detections from any domain.

By default, remoting detection will ignore any detection message the it receives from a server invoker running within its own jvm. To disable this, add an element called 'local' to the detector configuration (alongside the domain element) to indicate should accept detection messages from local server invokers. This will be false by default, so maintains the same behavior as previous releases. For example:

        <domains>
            <domain>domain1</domain>
            <domain>domain2</domain>
        </domains>
        <local/> 

An example entry of a Multicast detector in the jboss-service.xml that accepts detections only from the roxanne and sparky domains using port 5555, including servers in the same jvm, is as follows:

         <mbean code="org.jboss.remoting.detection.multicast.MulticastDetector"
               name="jboss.remoting:service=Detector,transport=multicast">
            <attribute name="Port">5555</attribute>
            <attribute name="Configuration">
              <domains>
                <domain>roxanne</domain>
                <domain>sparky</domain>
              </domains>
              <local/>
            </attribute>
         </mbean>
      

Global Detector Configuration

The following are configuration attributes for all the remoting detectors.

DefaultTimeDelay - amount of time, in milliseconds, which can elapse without receiving a detection event before suspecting that a server is dead and performing an explicit invocation on it to verify it is alive. If this invocation, or ping, fails, the server will be removed from the network registry. The default is 5000 milliseconds.

HeartbeatTimeDelay - amount of time to wait between sending (and sometimes receiving) detection messages. The default is 1000 milliseconds.

JNDIDetector

Port - port to which detector will connect for the JNDI server.

Host - host to which the detector will connect for the JNDI server.

ContextFactory - context factory string used when connecting to the JNDI server. The default is org.jnp.interfaces.NamingContextFactory .

URLPackage - url package string to use when connecting to the JNDI server. The default is org.jboss.naming:org.jnp.interfaces .

CleanDetectionNumber - Sets the number of detection iterations before manually pinging remote server to make sure still alive. This is needed since remote server could crash and yet still have an entry in the JNDI server, thus making it appear that it is still there. The default value is 5.

Can either set these programmatically using setter method or as attribute within the remoting-service.xml (or anywhere else the service is defined). For example:

         <mbean code="org.jboss.remoting.detection.jndi.JNDIDetector"
               name="jboss.remoting:service=Detector,transport=jndi">
            <attribute name="Host">localhost</attribute>
            <attribute name="Port">5555</attribute>
         </mbean>
      

If the JNDIDetector is started without the Host attribute being set, it will try to start a local JNP instance (the JBoss JNDI server implementation) on port 1088.

MulticastDetector

DefaultIP - The IP that is used to broadcast detection messages on via multicast. To be more specific, will be the ip of the multicast group the detector will join. This attribute is ignored if the Address has already been set when started. Default is 224.1.9.1.

Port - The port that is used to broadcast detection messages on via multicast. Default is 2410.

BindAddress - The address to bind to for the network interface.

Address - The IP of the multicast group that the detector will join. The default will be that of the DefaultIP if not explicitly set.

4.4. Transports (Invokers)

4.4.1. Server Invokers

The following configuration properties are common to all the current server invokers.

serverBindAddress - The address on which the server binds to listen for requests. The default is an empty value which indicates the server should be bound to the host provided by the locator url, or if this value is null, the local host as provided by InetAddress.getLocalHost() .

serverBindPort - The port to listen for requests on. A value of 0 or less indicates that a free anonymous port should be chosen.

maxNumThreadsOneway - specifies the maximum number of threads to be used within the thread pool for accepting one way invocations on the server side. This property will only be used in the case that the default thread pool is used. If a custom thread pool is set, this property will have no meaning. This property can also be retrieved or set programmatically via the MaxNumberOfOnewayThreads property.

onewayThreadPool - specifies either the fully qualified class name for a class that implements the org.jboss.util.threadpool.ThreadPool interface or the JMX ObjectName for an MBean that implements the org.jboss.util.threadpool.ThreadPool interface. This will replace the default org.jboss.util.threadpool.BasicThreadPool used by the server invoker.

Note that this value will NOT be retrieved until the first one-way (server side) invocation is made. So if the configuration is invalid, will not be detected until this first call is made. The thread pool can also be accessed or set via the OnewayThreadPool property programmatically.

Important to note that the default thread pool used for the one-way invocations on the server side will block the calling thread if all the threads in the pool are in use until one is released.

4.4.2. Configurations affecting the invoker client

There are some configurations which will impact the invoker client. These will be communicated to the client invoker via parameters in the Locator URI. These configurations can not be changed during runtime, so can only be set up upon initial configuration of the server invoker on the server side. The following is a list of these and their effects.

clientConnectPort - the port the client will use to connect to the remoting server. This would be needed in the case that the client will be going through a router that forwards requests made externally to a different port internally.

clientConnectAddress - the ip or hostname the client will use to connect to the remoting server. This would be needed in the case that the client will be going through a router that forwards requests made externally to a different ip or host internally.

If no client connect address or server bind address specified, will use the local host's address (via InetAddress.getLocalHost().getHostAddress() ).

4.4.3. How the server bind address and port is ultimately determined

If the serverBindAddress property is set, it will be used for binding. If the serverBindAddress is not set, but the clientConnectAddress property is set, the server invoker will bind to local host address. If neither the serverBindAddress nor the clientConnectAddress properties are set, then will try to bind to the host specified within the InvokerLocator. If the host value of the InvokerLocator is also not set, will bind to local host.

If there is a system property called 'remoting.bind_by_host' and if is false, will bind by IP address instead of host. Otherwise will use host name. This only applies when configured address is 0.0.0.0. To facilitate setting this property, the following static variable is defined in InvokerLocator:

           public static final String BIND_BY_HOST = "remoting.bind_by_host";
        

If the serverBindPort property is set, it will be used. If this value is 0 or a negative number, then the next available port will be found and used. If the serverBindPort property is not set, but the clientConnectPort property is set, then the next available port will be found and used. If neither the serverBindPort nor the clientConnectPort is set, then the port specified in the original InvokerLocator will be used. If this is 0 or a negative number, then the next available port will be found and used. In the case that the next available port is used because either the serverBindPort or the original InvokerLocator port value was either 0 or negative, the InvokerLocator will be updated to reflect the new port value.

4.4.4. Socket Invoker

The following configuration properties can be set at any time, but will not take effect until the socket invoker, on the server side, is stopped and restarted.

socketTimeout - The socket timeout value passed to the Socket.setSoTimeout() method. The default on the server side is 60000 (one minute). If the socketTimeout parameter is set, its value will also be passed to the client side (see below).

backlog - The preferred number of unaccepted incoming connections allowed at a given time. The actual number may be greater than the specified backlog. When the queue is full, further connection requests are rejected. Must be a positive value greater than 0. If the value passed if equal or less than 0, then the default value will be assumed. The default value is 200.

numAcceptThreads - The number of threads that exist for accepting client connections. The default is 1.

maxPoolSize - The number of server threads for processing client. The default is 300.

serverSocketClass - specifies the fully qualified class name for the custom SocketWrapper implementation to use on the server.

Configurations affecting the Socket invoker client

There are some configurations which will impact the socket invoker client. These will be communicated to the client invoker via parameters in the Locator URI. These configurations can not be changed during runtime, so can only be set up upon initial configuration of the socket invoker on the server side. The following is a list of these and their effects.

enableTcpNoDelay - can be either true or false and will indicate if client socket should have TCP_NODELAY turned on or off. TCP_NODELAY is for a specific purpose; to disable the Nagle buffering algorithm. It should only be set for applications that send frequent small bursts of information without getting an immediate response; where timely delivery of data is required (the canonical example is mouse movements). The default is false.

socketTimeout - The socket timeout value passed to the Socket.setSoTimeout() method. The default on the client side is 1800000 (or 30 minutes).

clientMaxPoolSize - the client side maximum number of threads. The default is 10.

clientSocketClass - specifies the fully qualified class name for the custom SocketWrapper implementation to use on the client. Note, will need to make sure this is marked as a client parameter (using the 'isParam' attribute). Making this change will not affect the marshaller/unmarshaller that is used, which may also be a requirement.

An example of locator uri for a socket invoker that has TCP_NODELAY set to false and the client’s max pool size of 30 would be:

socket://test.somedomain.com:8084/?enableTcpNoDelay=false&maxPoolSize=30

To reiterate, these client configurations can only be set within the server side configuration and will not change during runtime.

4.4.5. SSL Socket Invoker

Supports all the configuration attributes as the Socket Invoker, plus the following:

serverSocketFactory - Sets the server socket factory. If want ssl support use a server socket factory that supports ssl. The only requirement is that the server socket factory value must be an ObjectName, meaning the server socket factory implementation must be an MBean and also MUST implement the org.jboss.remoting.security.ServerSocketFactoryMBean interface.

4.4.6. RMI Invoker

registryPort - the port on which to create the RMI registry. The default is 3455. This also needs to have the isParam attribute set to true.

4.4.7. HTTP Invoker

The HTTP server invoker implementation is based on the Apache Tomcat connector components which support GET, POST, HEAD, OPTIONS, and HEAD method types and keep-alive. Therefore, most any configuration allowed for Tomcat can be configured for the remoting HTTP server invoker. For more information on the configuration attributes available for the Tomcat connectors, please refer to http://tomcat.apache.org/tomcat-5.5-doc/config/http.htm. So for example, if wanted to set the maximum number of threads to be used to accept incoming http requests, would use the 'maxThreads' attribute. The only exception when should use remoting configuration over the Tomcat configuration is for attribute 'address' (use serverBindAddress instead) and attribute 'port' (use serverBindPort instead).

Note: The http invoker no longer has the configuration attributes 'maxNumThreadsHTTP' or 'HTTPThreadPool' as thread pooling is now handled within the Tomcat connectors, which does not expose external API for setting these.

Since the remoting HTTP server invoker implementation is using Tomcat connectors, is possible to swap out the Tomcat protocol implementations being used. By default, the protocol being used is org.apache.coyote.http11.Http11Protocol. However, it is possible to switch to use the org.apache.coyote.http11.Http11AprProtocol protocol, which is based on the Apache Portable Runtime (see http://tomcat.apache.org/tomcat-5.5-doc/apr.html and http://apr.apache.org/ for more details). If want to use the APR implementation, simply put the tcnative-1.dll (or tcnative-1.so) on the system path so can be loaded. The APR native binaries can be found at http://tomcat.heanet.ie.

Note: There is a bug with release 1.1.1 of APR where get an error upon shutting down (see JBREM-277 for more information). This does not impact anything while running, but is still an issue when shutting down (as upon starting up again, can get major problems). This should be fixed in a later release of APR and since can just replace the 1.1.1 version of tcnative-1.dll with the new one.

Client request headers

The HTTP Invoker allows for some of the properties to be passed as request headers from client caller. The following are possible http headers and what they mean:

sessionId - is the remoting session id to identify the client caller. If this is not passed, the HTTP server invoker will try to create a session id based on information that is passed. Note, this means if the sessionId is not passed as part of the header, there is no gurantee that the sessionId supplied to the invocation handler will always indicate the request from the same client.

subsystem - the subsystem to call upon (which invoker handler to call upon). If there is more than one handler per Connector, this will need to be set (otherwise will just use the only one available).

These request headers are set automatically when using a remoting client, but if using another client to send request to the HTTP server invoker, may want to add these headers.

Exception Handling

When using remoting on the client side to connect to a remoting server (or any web server for that matter) via the http transport, if the server returns a response code greater than 400, the remoting client will read the error stream and return that as the response. Thus, the response returned will be of type java.lang.Exception. NOTE: this does NOT mean that the call to the Client's invoke() method will throw the exception, but instead will return the actual Exception object instance as a normally returned response. Therefore, is important that if want to check if response is an error instead of normal response, will need to look at the response code put in the metadata Map passed to the invoke() method on the Client instance. See the HTTP Handler section above for more details.

4.4.8. HTTPS Invoker

Supports all the configuration attributes as the HTTP Invoker, plus the following:

serverSocketFactory - Sets the server socket factory. If want ssl support, use a server socket factory that supports ssl. The only requirement is that the server socket factory value must be an ObjectName, meaning the server socket factory implementation must be an MBean and also MUST implement the org.jboss.remoting.security.ServerSocketFactoryMBean interface.

SSLImplementation - Sets the Tomcat SSLImplementation to use. This should always be org.jboss.remoting.transport.coyote.ssl.RemotingSSLImplementation.

4.4.9. HTTP(S) Client Invoker - proxy and basic authentication

This section covers configuration specific to the HTTP Client Invoker only and is NOT related to HTTP(S) invoker configuration on the server side (via service xml).

proxy

There are a few ways in which to enable http proxy using the HTTP client invoker. The first is simply to add the following properties to the metadata Map passed on the Client's invoke() method: http.proxyHost and http.proxyPort

An example would be:

               Map metadata = new HashMap();
               ...

               // proxy info
               metadata.put("http.proxyHost", "ginger");
               metadata.put("http.proxyPort", "80");

               ...

               response = client.invoke(payload, metadata);
            

The http.proxyPort property is not required and if not present, will use default of 80.

The other way to enable use of an http proxy server from the HTTP client invoker is to set the following system properties (either via System.setProperty() method call or via JVM arguments): http.proxyHost, http.proxyPort, and proxySet

An example would be setting the following JVM arguments:

-Dhttp.proxyHost=ginger -Dhttp.proxyPort=80 -DproxySet=true

Note: when testing with Apache 2.0.48 (mod_proxy and mod_proxy_http), all of the properties above were required.

Setting the system properties will take precedence over setting the metadata Map.

Basic authentication - direct and via proxy

The HTTP client invoker also has support for BASIC authentication for both proxied and non-proxied invocations. For proxied invocations, the following properties need to be set: http.proxy.username and http.proxy.password.

For non-proxied invocations, the following properties need to be set: http.basic.username and http.basic.password.

For setting either proxied or non-proxied properties, can be done via the metadata map or system properties (see setting proxy properties above for how to). However, for authentication properties, values set in the metadata Map will take precedence over those set within the system properties.

Note: Only the proxy authentication has been tested using Apache 2.0.48; non-proxied authentication has not.

Since there are many different ways to do proxies and authentication in this great world of web, not all possible configurations have been tested (or even supported). If you find a particular problem or see that a particular implementation is not supported, please enter an issue in Jira ( http://jira.jboss.com ) under the JBossRemoting project, as this is where bugs and feature requests belong. If after reading the documentation have unanswered questions about how to use these features, please post them to the remoting forum ( http://www.jboss.org/index.html?module=bb&op=viewforum&f=176 ).

Host name verification

During the SSL handshake when making client calls using https transport, if the URL's hostname and the server's identification hostname mismatch, a javax.net.ssl.HostnameVerifier implementation will be called to determine if this connection should be allowed. The default implementation will not allow this. To override this behavior to allow this by changing the HostnameVerifier implementation, can use the 'org.jboss.security.ignoreHttpsHost' property'. This property can either be set using a system property or within the metadata Map passed to the Client's invoke() method (which will override both the default value and the setting from the system property).

4.4.10. Servlet Invoker

The servlet invoker is a server invoker implementation that uses a servlet running within a web container to accept initial client invocation requests. The servlet request is then passed on to the servlet invoker for processing.

The deployment for this particular server invoker is a little different than the other server invokers since a web deployment is also required. To start, the servlet invoker will need to be configured and deployed. This can be done by adding the Connector MBean service to an existing service xml or creating a new one. The following is an example of how to declare a Connector that uses the servlet invoker:

           <mbean code="org.jboss.remoting.transport.Connector"
                  xmbean-dd="org/jboss/remoting/transport/Connector.xml"
                  name="jboss.remoting:service=Connector,transport=Servlet"
                  display-name="Servlet transport Connector">

              <attribute name="InvokerLocator">
                 servlet://localhost:8080/servlet-invoker/ServerInvokerServlet
              </attribute>

              <attribute name="Configuration">
                 <config>
                    <handlers>
                       <handler subsystem="test">
                          org.jboss.test.remoting.transport.web.WebInvocationHandler
                       </handler>
                    </handlers>
                 </config>
              </attribute>
           </mbean>
        

An important point of configuration to note is that the value for the InvokerLocator attribute is the exact url used to access the servlet for the servlet invoker (more on how to define this below), with the exception of the protocol being servlet instead of http. This is important because if using automatic discovery, this is the locator url that will be discovered and used by clients to connect to this server invoker.

The next step is to configure and deploy the servlet that fronts the servlet invoker. The pre-built deployment file for this servlet is the servlet-invoker.war file (which can be found in the release distribution or under the output/lib/ directory if doing a source build). By default, it is actually an exploded war, so the servlet-invoker.war is actually a directory so that can be more easily configured (feel free to zip up into an actual war file if prefer). In the WEB-INF directory is located the web.xml file. This is a standard web configuration file and should look like:

           <?xml version="1.0" encoding="UTF-8"?>
           <!DOCTYPE web-app PUBLIC
               "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
               "http://java.sun.com/dtd/web-app_2_3.dtd">

           <!--The the JBossRemoting server invoker servlet web.xml descriptor-->
           <web-app>
              <servlet>
                 <servlet-name>ServerInvokerServlet</servlet-name>
                 <description>The ServerInvokerServlet receives requests via HTTP protocol
                    from within a web container and passes it onto the ServletServerInvoker for processing.
                 </description>
                 <servlet-class>
                    org.jboss.remoting.transport.servlet.web.ServerInvokerServlet
                 </servlet-class>
                 <init-param>
                    <param-name>invokerName</param-name>
                    <param-value>
                       jboss.remoting:service=invoker,transport=servlet
                    </param-value>
                    <description>The servlet server invoker</description>
                 </init-param>
                 <load-on-startup>1</load-on-startup>
              </servlet>
              <servlet-mapping>
                 <servlet-name>ServerInvokerServlet</servlet-name>
                 <url-pattern>/ServerInvokerServlet/*</url-pattern>
              </servlet-mapping>
           </web-app>
        

This file can be changed to meet any web requirements you might have, such as adding security or changing the actual url context that the servlet maps to. If the url that the servlet maps to is changed, will need to change the value for the InvokerLocator in the Connector configuration mentioned above. Also note that there is a parameter, invokerName, that has the value of the object name of the servlet server invoker. This is what the ServerInvokerServlet uses to look up the server invoker which it will pass the requests on to.

Due to the way the servlet invoker is currently configured and deployed, it must run within the JBoss application server and is not portable to other web servers.

Exception handling

If the ServletServerInvoker catches any exception thrown from the invocation handler invoke() call, it will send an error to the client with a status of 500 and include the original exception message as its error message. From the client side, the client invoker will actually throw a CannotConnectException, which will have root exception as its cause. The cause should be an IOException with the server's message. For example, the stack trace from the exception thrown within the test case org.jboss.remoting.transport.servlet.test.ServletInvokerTestClient is:

               org.jboss.remoting.CannotConnectException: Can not connect http client invoker.
               at org.jboss.remoting.transport.http.HTTPClientInvoker.useHttpURLConnection(HTTPClientInvoker.java:154)
               at org.jboss.remoting.transport.http.HTTPClientInvoker.transport(HTTPClientInvoker.java:68)
               at org.jboss.remoting.RemoteClientInvoker.invoke(RemoteClientInvoker.java:113)
               at org.jboss.remoting.Client.invoke(Client.java:221)
               at org.jboss.remoting.Client.invoke(Client.java:184)
               at
               org.jboss.remoting.transport.servlet.test.ServletInvokerTestClient.testInvocation(ServletInvokerTestClient.java:65)
               at
               org.jboss.remoting.transport.servlet.test.ServletInvokerTestClient.main(ServletInvokerTestClient.java:98)
               at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
               at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
               at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
               at java.lang.reflect.Method.invoke(Method.java:324)
               at com.intellij.rt.execution.application.AppMain.main(AppMain.java:78)
               Caused by: java.io.IOException: Server returned HTTP response code: 500 for URL:
               http://localhost:8080/servlet-invoker/ServerInvokerServlet
               at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:791)
               at org.jboss.remoting.transport.http.HTTPClientInvoker.useHttpURLConnection(HTTPClientInvoker.java:139)
               ... 11 more
            

Issues

One of the issues of using Servlet invoker is that the invocation handlers (those that implement ServerInvocationHandler) can not return very much detail in regards to a web context. For example, the content type used for the response is the same as that of the request.

4.4.11. Multiplex Invoker

The multiplex invoker is intended to replicate the functionality of the socket invoker with the added feature that it supports multiple streams of communication over a single pair of sockets. Multiplexing may be motivated by, for example, a desire to conserve socket resources or by firewall restrictions on port availability. This additional service is made possible by the Multiplex subproject, which provides "virtual" sockets and "virtual" server sockets. Please refer to the Multiplex documentation at

for further details.

In a typical multiplexed scenario a Client on a client host, through a MultiplexClientInvoker C, could make synchronous method invocations to a MultiplexServerInvoker on a server host, and at the same time (and over the same TCP connection) asynchronous push callbacks could be made to a MultiplexServerInvoker S on the client host. In this, the Prime Scenario, which motivated the creation of the multiplex invoker, C and S use two different virtual sockets but share the same port and same actual socket. We say that C and S belong to the same invoker group.

One of the primary design goals of the Multiplex subsystem is for virtual sockets and virtual server sockets to demonstrate behavior as close as possible to their real counterparts, and, indeed, they implement complete socket and server socket APIs. However, they are necessarily different in some respects, and it follows that the multiplex invoker is somewhat different than the socket invoker. In particular, there are three areas specific to the multiplex invoker that must be understood in order to use it effectively:

  1. Establishing on the server an environment prerequisite for creating multiplex connections

  2. Configuring the client for multiplexed method invocations and callbacks

  3. Shutting down invoker groups.

4.4.11.1. Setting up the server

There are two kinds of MultiplexServerInvokers, master and virtual, corresponding to the two kinds of virtual server sockets: MasterServerSocket and VirtualServerSocket. Briefly, the difference between the two virtual server socket classes is that a MasterServerSocket is derived from java.net.ServerSocket and its accept() method is implemented by way of the inherited method super.accept(). A MasterServerSocket can accept connect requests from multiple machines. A VirtualServerSocket, on the other hand, is based on an actual socket connected to another actual socket on some host H, and consequently a VirtualServerSocket can accept connect requests only from H.

Each multiplex connection depends on a pair of connected real sockets, one on the client host and one on the server host, and this connection is created when an actual socket contacts an actual server socket. It follows that a multiplex connection begins with a connection request to a MasterServerSocket. Once the connection is established, it is possible to build up virtual socket groups, consisting of virtual sockets (and at most one VirtualServerSocket) revolving around the actual socket at each end of the connection. Each virtual socket in a socket group at one end is connected to a virtual socket in the socket group at the other end.

Master and virtual MultiplexServerInvokers assume the characteristics of their server sockets: MasterServerSocket and VirtualServerSocket, respectively. That is, a master MultiplexServerInvoker can accept requests from any host, while a virtual MultiplexServerInvoker can accept requests only from the particular host to which it has a multiplex connection. Since a multiplex connection begins with a connection request to a MasterServerSocket, it follows that the use of the multiplex invoker must begin with a connection request from the client (made by either a MultiplexClientInvoker or a virtual MultiplexServerInvoker: see below) to a master MultiplexServerInvoker on the server. The master MultiplexServerInvoker responds by "cloning" itself (metaphorically, not necessarily through the use of clone()) into a virtual MultiplexServerInvoker with the same parameters and same set of invocation handlers but with a VirtualServerSocket belonging to a new socket group. In so doing the master MultiplexServerInvoker builds up a server invoker farm of virtual MultiplexServerInvokers, each in contact with a different MultiplexClientInvoker over a distinct multiplex connection. The virtual MultiplexServerInvokers do the actual work of responding to method invocation requests, sent by their corresponding MultiplexClientInvokers through virtual sockets in a socket group at the client end of a multiplex connection to virtual sockets created by the VirtualServerSocket in the socket group at the server end of the connection. Note that virtual MultiplexServerInvokers share data structures with the master, so that registering invocation handlers with the master makes them available to the members of the farm. The members of a master MultiplexServerInvoker's invoker farm are accessible by way of the methods

  1. MultiplexServerInvoker.getServerInvokers() and

  2. MultiplexServerInvoker.getServerInvoker(InetSocketAddress)

the latter of which returns a virtual MultiplexServerInvoker keyed on the address to which its VirtualServerSocket is connected. When the master MultiplexServerInvoker shuts down, its farm of virtual invokers shuts down as well

There are two ways of constructing a virtual MultiplexServerInvoker, one being the cloning method just discussed. It is also possible to construct one directly. Once a multiplex connection is established, a virtual MultiplexServerInvoker can be created with a VirtualServerSocket belonging to a socket group at one end of the connection. The MultiplexServerInvoker constructor determines whether to create a virtual or master invoker according to the presence or absence of certain parameters, discussed below, that may be added to its InvokerLocator. Server rules 1 through 3 described below result in the construction of a virtual MultiplexServerInvoker, and server rule 4 (the absence of these parameters) results in the construction of a master MultiplexServerInvoker.

Setting up the server, then, is simply a matter of starting a master MultiplexServerInvoker with a simple InvokerLocator, unadorned with any parameters specific to the multiplex invoker. As always, the server invoker is not created directly but by way of a Connector, as in the following:

            Connector connector = new Connector();
            Connector.setInvokerLocator("multiplex://demo.jboss.com:8080");
            Connector.create()
            Connector.start()
            

4.4.11.2. Setting up the client

Before multiplex connections can be established, a master MultiplexServerInvoker must be created as described in the previous section. For example, the Prime Scenario would begin with starting a master MultiplexServerInvoker on the server host, followed by starting, on the client host, a MultiplexClientInvoker C and a virtual MultiplexServerInvoker S (in either order). The first to start initiates a multiplex connection to the master MultiplexServerInvoker and requests the creation of a virtual MultiplexServerInvoker. Note that it is crucial for C and S to know that they are meant to share a multiplex connection, i.e., that they are meant to belong to the same invoker group. Consider the following attempt to set up a shared connection between hosts bluemonkey.acme.com and demo.jboss.com. First, C is initialized on host bluemonkey.acme.com with the InvokerLocator multiplex://demo.jboss.com:8080, and, assuming the absence of an existing multiplex connection to demo.jboss.com:8080, a new virtual socket group based on a real socket bound to an arbitrary port, say 32000, is created. Then S is initialized with InvokerLocator multiplex://bluemonkey.acme.com:4444, but since it needs to bind to port 4444, it is unable to share the existing connection. [Actually, the example is slightly deceptive, since multiplex://bluemonkey.acme.com:4040 would result in the creation of a master MultiplexServerInvoker. But if it were suitably extended with the parameters discussed below so that a virtual MultiplexServerInvoker were created, the virtual invoker would be unable to share the existing connection.]

So C and S need to agree on the address and port of the real socket underlying the virtual socket group they are intended to share on the client host and the address and port of the real socket underlying the peer virtual socket group on the server host. Or, more succintly, they must know that they are meant to belong to the same invoker group. Note the relationship between an invoker group and the virtual socket group which supports it: a MultiplexClientInvoker uses virtual sockets in its underlying virtual socket group, and a MultiplexServerInvoker in an invoker group has a VirtualServerSocket that creates virtual sockets in the underlying virtual socket group.

C and S each get half of the information necessary to identify their invoker group directly from their respective InvokerLocators. In particular, C gets the remote address and port, and S gets the binding address and port. The additional information may be provided through the use of invoker group parameters, which may be communicated to C and S in one of two ways:

  1. they may be appended to the InvokerLocator passed to the Client which creates C and/or to the InvokerLocator passed to the Connector which creates S

  2. they may be stored in a configuration Map which is passed to the Client and/or Connector.

In either case, there are two ways in which the missing information can be supplied to C and S:

  1. The information can be provided explicitly by way of invoker group parameters:

    1. multiplexBindHost and multiplexBindPort parameters can be passed to C, and

    2. multiplexConnectHost and multiplexConnectPort parameters can be passed to S.

  2. C and S can be tied together by giving them the same multiplexId, supplied by invoker group parameters:

    1. clientMultiplexId, for the MultiplexClientInvoker, and

    2. serverMultiplexId, for the MultiplexServerInvoker.

    Giving them matching multiplexIds tells them that they are meant to belong to the same invoker group and that they should provide the missing information to each other.

The behavior of a starting MultiplexClientInvoker C is governed by the following four client rules:

  1. If C has a clientMultiplexId parameter, it will use it to attempt to find a MultiplexServerInvoker S with a serverMultiplexId parameter with the same value. If it succeeds, it will retrieve binding host and port values, create or reuse a suitable multiplex connection to the server, and start. Moreover, if S was unable to start because of insufficient information (server rule 3), then C will supply the missing information and S will start. Note that in this situation C will ignore any multiplexBindHost and multiplexBindPort parameters passed to it.

  2. If C does not find a MultiplexServerInvoker through a multiplexId (either because it did not get a clientMultiplexId parameter or because there is no MultiplexServerInvoker with a matching multiplexId), but it does have multiplexBindHost and multiplexBindPort parameters, then it will create or reuse a suitable multiplex connection to the server, and start. Also, if it has a multiplexId, it will advertise itself for the benefit of a MultiplexServerInvoker that may come along later (see server rule 1).

  3. If C has a multiplexId and neither finds a MultiplexServerInvoker with a matching multiplexId nor has multiplexBindHost and multiplexBindPort parameters, then it will not start, but it will advertise itself so that it may be found later by a MultiplexServerInvoker (see server rule 1).

  4. If C has neither clientMultiplexId nor multiplexBindHost and multiplexBindPort parameters, it will create or reuse a multiplex connection from an arbitrary local port to the host and port given in its InvokerLocator, and start.

Similarly, the behavior of a starting MultiplexServerInvoker S is governed by the following four server rules:

  1. If S has a serverMultiplexId parameter, it will use it to attempt to find a MultiplexClientInvoker C with a matching clientMultiplexId. If it succeeds, it will retrieve server host and port values, create a VirtualServerSocket, create or reuse a suitable multiplex connection to the server, and start. Moreover, if C was unable to start due to insufficient information (client rule 3), then S will supply the missing information and C will start. Note that in this situation S will ignore multiplexConnectHost and multiplexConnectPort parameters, if any, in its InvokerLocator.

  2. If S does not find a MultiplexClientInvoker through a multiplexId (either because it did not get a serverMultiplexId parameter or because there is no MultiplexClientInvoker with a matching multiplexId), but it does have multiplexConnectHost and multiplexConnectPort parameters, then it will create a VirtualServerSocket, create or reuse a suitable multiplex connection to the server, and start. Also, if it has a multiplexId, it will advertise itself for the benefit of a MultiplexClientInvoker that may come along later (see client rule 1).

  3. If S has a multiplexId and neither finds a MultiplexClientInvoker with a matching multiplexId nor has multiplexConnectHost and multiplexConnectPort parameters, then it will not start, but it will advertise itself so that it may be found later by a MultiplexClientInvoker (see client rule 1).

  4. If S has neither serverMultiplexId nor multiplexConnectHost and multiplexConnectPort parameters, it will create a MasterServerSocket bound to the host and port in its InvokerLocator and start.

4.4.11.2.1. Notes
  1. Like server invokers, client invokers are not started directly but are started indirectly through calls to Client(InvokerLocator locator), such as:

                Client client = new Client("multiplex://demo.jboss.com:8080/?clientMultiplexId=id0");
                client.connect();
                      

    N.B. For the multiplex invoker, it is important to call Client.connect(). Otherwise, the last MultiplexClientInvoker that leaves an invoker group will not get a chance to shut the group down.

  2. It should not be inferred that MultiplexClientInvokers and MultiplexServerInvokers belong to the same invoker group only if they are required to do so by invoker group parameters. In fact, if two Clients are created with the InvokerLocator multiplex://demo.jboss.com, the second one, lacking any constraints on its binding address and port, is certainly not prevented from sharing a connection with the first. Rather, the function of the invoker group parameters is to force MultiplexClientInvokers and MultiplexServerInvokers to share a connection.

  3. There are situations in which the method of passing parameters by way of the configuration map is preferable to appending them to an InvokerLocator. One of the functions of an InvokerLocator is to identify a server, and modifying the content of its InvokerLocator may interfere with the ability to locate the server. For example, one of the features of JBoss Remoting is the substitution of method calls for remote invocations when it discovers that a server runs in the same JVM as the client. However, appending multiplex parameters to the InvokerLocator by which the server is identified will prevent the Remoting runtime from recognizing the local presence of the server, and the optimization will not occur.

  4. It is possible, and convenient, to set up a multiplexing scenario using no parameters other than clientMultiplexId and serverMultiplexId. Note, however, that in this case neither the Clients nor the Connector will be fully initialized until after both have been started. If the Clients and the Connector are to be started independently, then the other parameters must be used. N.B. If a Client depends on Connector in the same invoker group to supply binding information, it is an error to call methods such as Client.connect() and Client.invoke() until the Connector has been started.

  5. Clients and the optional Connector may be created (and the Connector started) in any order.

4.4.11.3. Shutting down invoker groups.

A virtual socket group will shut down, releasing a real socket and a number of threads, when (1) its last member has closed and (2) the socket group at the remote end of the multiplex connection agrees to the proposed shut down. The second condition prevents a situation in which a new virtual socket tries to join what it thinks is a viable socket group at the same time that the peer socket group is shutting down. So for a virtual socket group to shut down, all members at both ends of the connection must be closed.

The implication of this negotiated shutdown mechanism is that as long as the VirtualServerSocket used by a virtual MultiplexServerInvoker remains open, resources at the client end of the connection cannot be freed, and for this reason it is important to understand how to close virtual MultiplexServerInvokers.

There are three ways in which a virtual MultiplexServerInvoker that belongs to a master MultiplexServerInvoker's invoker farm can shut down.

  • When a master MultiplexServerInvoker is closed, it closes all of the virtual MultiplexServerInvokers it created.

  • A virtual MultiplexServerInvoker can be retrieved by calling either MultiplexServerInvoker.getServerInvokers() or MultiplexServerInvoker.getServerInvoker(InetSocketAddress) on its master MultiplexServerInvoker and then closed directly.

  • When the accept() method of its VirtualServerSocket times out, and when it detects that all multiplex invokers in the invoker group at the client end of the connection have shut down, a virtual MultiplexServerInvoker will shut itself down. Note that when all members leave an invoker group, it is guaranteed not to be revived, i.e., no new members may join.

The third method insures that without any explicit intervention, closing all multiplex invokers on the client (by way of calling Client.disconnect() and Connector.stop()) is guaranteed to result in the eventual release of resources. The timeout period may be adjusted by setting the socketTimeout parameter (see below). Alternatively, the second method, in conjunction with the use of MultiplexServerInvoker.isSafeToShutdown(), which returns true on MultiplexServerInvoker M if and only if (1) M is not virtual, or (2) all of the multiplex invokers in the invoker group at the client end of M's connection have shut down. For example, a thread could be dedicated to looking for useless MultiplexServerInvokers and terminating them before their natural expiration through timing out.

4.4.11.4. Examples

The following are examples of setting up a client for multiplexed synchronous and asynchronous communication. They each assume the existence of a master MultiplexServerInvoker running on demo.jboss.com:8080.

For complete examples see the section Multiplex invokers.

  1. A MultiplexClientInvoker C starts first:

                String parameters = "multiplexBindHost=localhost&multiplexBindPort=7070&clientMultiplexId=demoId1";
                String locatorURI = "multiplex://demo.jboss.com:8080/?" + parameters;
                InvokerLocator locator = new InvokerLocator(locatorURI);
                Client client = new Client(locator);
                client.connect();
                   

    and then it is found by a MultiplexServerInvoker with a matching multiplexId, which joins C's invoker group and starts:

                Connector connector = new Connector();
                String parameters = "serverMultiplexId=demoId1";
                String locatorURI = "multiplex://localhost:7070/?" + parameters;
                InvokerLocator locator = new InvokerLocator(locatorURI);
                connector.setInvokerLocator(locator.getLocatorURI());
                connector.create();
                connector.start();
                   
  2. A MultiplexClientInvoker C starts:

                String parameters = "multiplexBindHost=localhost&multiplexBindPort=7070";
                String locatorURI = "multiplex://demo.jboss.com:8080/?" + parameters;
                InvokerLocator locator = new InvokerLocator(locatorURI);
                Client client = new Client(locator);
                client.connect();
                      

    and a MultiplexServerInvoker S starts independently, joining C's invoker group by virtue of having matching local and remote addresses and ports:

                Connector connector = new Connector();
                String parameters = "multiplexConnectHost=demo.jboss.com&multiplexConnectPort=8080";
                String locatorURI = "multiplex://localhost:7070/?" + parameters;
                InvokerLocator locator = new InvokerLocator(locatorURI);
                connector.setInvokerLocator(locator.getLocatorURI());
                connector.create();
                connector.start();
                   
  3. A MultiplexClientInvoker C is created but does not start:

                String parameters = "clientMultiplexId=demoId2";
                String locatorURI = "multiplex://demo.jboss.com:8080/?" + parameters;
                InvokerLocator locator = new InvokerLocator(locatorURI);
                Client client = new Client(locator);
                   

    and then a MultiplexServerInvoker S is created with a matching multiplexId, allowing both C and S to start:

                Connector connector = new Connector();
                String parameters = "serverMultiplexId=demoId2";
                String locatorURI = "multiplex://localhost:7070/?" + parameters;
                InvokerLocator locator = new InvokerLocator(locatorURI);
                connector.setInvokerLocator(locator.getLocatorURI());
                connector.create();
                connector.start();
                client.connect();
                   

    Note the call to Client.connect() after the call to Connector.start().

  4. A MultiplexClientInvoker C starts in an invoker group based on a real socket bound to an arbitrary local port:

                String locatorURI = "multiplex://demo.jboss.com:8080";
                InvokerLocator locator = new InvokerLocator(locatorURI);
                Client client = new Client(locator);
                client.connect();
                   

    and then a MultiplexServerInvoker S starts independently:

                Connector connector = new Connector();
                String locatorURI = "multiplex://localhost:7070";
                InvokerLocator locator = new InvokerLocator(locatorURI);
                connector.setInvokerLocator(locator.getLocatorURI());
                connector.create();
                connector.start();
                   

    Note that S creates a MasterServerSocket rather than a VirtualServerSocket in this case and so does not share a multiplex connection and does not belong to an invoker group.

  5. This is example 1, rewritten so that the invoker group parameters are passed by way of a configuration Map instead of InvokerLocators. A MultiplexClientInvoker C starts first:

                String locatorURI = "multiplex://demo.jboss.com:8080";
                InvokerLocator locator = new InvokerLocator(locatorURI);
                Map configuration = new HashMap();
                configuration.put(MultiplexInvokerConstants.MULTIPLEX_BIND_HOST_KEY, "localhost");
                configuration.put(MultiplexInvokerConstants.MULTIPLEX_BIND_PORT_KEY, "7070");
                configuration.put(MultiplexInvokerConstants.CLIENT_MULTIPLEX_ID_KEY, "demoId1");
                Client client = new Client(locator, configuration);
                client.connect();
                   

    and then it is found by a MultiplexServerInvoker with a matching multiplexId, which joins C's invoker group and starts:

                String locatorURI = "multiplex://localhost:7070";
                InvokerLocator locator = new InvokerLocator(locatorURI);
                Map configuration = new HashMap();
                configuration.put(MultiplexInvokerConstants.SERVER_MULTIPLEX_ID_KEY, "demoId1");
                Connector connector = new Connector(locator.getLocatorURI(), configuration);
                connector.create();
                connector.start();
                   

4.4.11.5. Configuration properties

There are four categories of configuration properties supported by the multiplex invoker.

  1. The following properties can be used to configure both master and virtual MultiplexorServerInvokers. They can be set at any time, but will not take effect until the invoker is stopped and restarted. A subset of the parameters applicable to the socket invoker is currently implemented.

    socketTimeout - The socket timeout value passed to the Socket.setSoTimeout() method and the ServerSocket.setSoTimeout() method. The default is 60000 (or 1 minute).

    numAcceptThreads - The number of threads that exist for accepting client connections. The default is 1.

  2. The following properties are intended to be passed to a MultiplexServerInvoker and then communicated to a corresponding MultiplexClientInvoker via parameters in the Locator URI. These configurations cannot be changed during runtime, so can only be set up upon initial configuration of the multiplex invoker on the server side. A subset of the parameters applicable to the socket invoker is currently implemented.

    socketTimeout - The socket timeout value passed to the Socket.setSoTimeout() method. The default is 1800000 (or 30 minutes).

  3. The following properties are intended to be passed to a virtual MultiplexServerInvoker to configure its multiplex connection. These properties are specific to the multiplex invoker.

    multiplexConnectHost - the name or address of the host to which the multiplex connection should be made.

    multiplexConnectPort - the port to which the multiplex connection should be made.

    serverMultiplexId - a string that associates a MultiplexServerInvoker with a MultiplexClientInvoker with which it should share a multiplex connection.

  4. The following properties are intended to be passed to a virtual MultiplexClientInvoker to configure its multiplex connection. These properties are specific to the multiplex invoker.

    multiplexBindHost - the host name or address to which the local end of the multiplex connection should be bound.

    multiplexBindPort - the port to which the local end of the multiplex connection should be bound

    clientMultiplexId - a string that associates a MultiplexClientInvoker with a MultiplexServerInvoker with which it should share a multiplex connection.

4.5. Marshalling

Marshalling of data can range from extremely simple to somewhat complex, depending on how much customization is needed. The following explains how marshallers/unmarshallers can be configured. Note that this applies for all the different transports, but will use the socket transport for examples.

The easiest way to configure marshalling is to specify nothing at all. This will prompt the remoting invokers to use their default marshaller/unmarshallers. For example, the socket invoker will use the SerializableMarshaller/SerializableUnMarshaller and the http invoker will use the HTTPMarshaller/HTTPUnMarshaller, on both the client and server side.

The next easiest way is to specify the data type of the marshaller/unmarshaller as a parameter to the locator url. This can be done by simply adding the key word 'datatype' to the url, such as:

socket://myhost:5400/?datatype=serializable

This can be done for types that are statically bound within the MarshalFactory , serializable and http, without requiring any extra coding, since they will be available to any user of remoting. However, is more likely this will be used for custom marshallers (since could just use the default data type from the invokers if using the statically defined types). If using custom marshaller/unmarshaller, will need to make sure both are added programmatically to the MarshalFactory during runtime (on both the client and server side). This can be done by the following method call within the MarshalFactory:

public static void addMarshaller(String dataType, Marshaller marshaller, UnMarshaller unMarshaller)
      

The dataType passed can be any String value desired. For example, could add custom InvocationMarshaller and InvocationUnMarshaller with the data type of 'invocation'. An example using this data type would then be:

socket://myhost:5400/?datatype=invocation

One of the problems with using a data type for a custom Marshaller/UnMarshaller is having to explicitly code the addition of these within the MarshalFactory on both the client and the server. So another approach that is a little more flexible is to specify the fully qualified class name for both the Marshaller and UnMarshaller on the locator url. For example:

socket://myhost:5400/?datatype=invocation&
            marshaller=org.jboss.invocation.unified.marshall.InvocationMarshaller&
            unmarshaller=org.jboss.invocation.unified.marshall.InvocationUnMarshaller
         

This will prompt remoting to try to load and instantiate the Marshaller and UnMarshaller classes. If both are found and loaded, they will automatically be added to the MarshalFactory by data type, so will remain in memory. Now the only requirement is that the custom Marshaller and UnMarshaller classes be available on both the client and server's classpath.

Another requirement of the actual Marshaller and UnMarshaller classes is that they have a void constructor. Otherwise loading of these will fail.

This configuration can also be applied using the service xml. If using declaration of invoker using the InvokerLocator attribute, can simply add the datatype, marshaller, and unmarshaller parameters to the defined InvokerLocator attribute value. For example:

      
         <attribute name="InvokerLocator">
            <![CDATA[socket://${jboss.bind.address}:8084/?datatype=invocation&
            marshaller=org.jboss.invocation.unified.marshall.InvocationMarshaller&
            unmarshaller=org.jboss.invocation.unified.marshall.InvocationUnMarshaller]]>
         </attribute>
      

If were using config element to declare the invoker, will need to add an attribute for each and include the isParam attribute set to true. For example:

         <invoker transport="socket">
            <attribute name="dataType" isParam="true">invocation</attribute>
            <attribute name="marshaller" isParam="true">
               org.jboss.invocation.unified.marshall.InvocationMarshaller
            </attribute>
            <attribute name="unmarshaller" isParam="true">
                  org.jboss.invocation.unified.marshall.InvocationUnMarshaller
            </attribute>
         </invoker>
      

This configuration is fine if the classes are present within the client's classpath. If they are not, can provide configuration for allowing clients to dynamically load the classes from the server. To do this, can use the parameter 'loaderport' with the value of the port you would like your marshal loader to run on. For example:

         <invoker transport="socket">
            <attribute name="dataType" isParam="true">invocation</attribute>
            <attribute name="marshaller" isParam="true">
               org.jboss.invocation.unified.marshall.InvocationMarshaller
            </attribute>
            <attribute name="unmarshaller" isParam="true">
               org.jboss.invocation.unified.marshall.InvocationUnMarshaller
            </attribute>
            <attribute name="loaderport" isParam="true">5401</attribute>
         </invoker>
      

When this parameter is supplied, the Connector will recognize this at startup and create a marshal loader connector automatically, which will run on the port specified. The locator url will be exactly the same as the original invoker locator, except will be using the socket transport protocol and will have all marshalling parameters removed (except the dataType). When the remoting client can not load the marshaller/unmarshaller for the specified data type, it will try to load them from the marshal loader service running on the loader port, including any classes they depend on. This will happen automatically and no coding is required (only the ability for the client to access the server on the specified loader port, so must provide access if running through firewall).

Compression marshalling

A compression marshaller/unmarshaller is available as well which uses gzip to compress and uncompress large payloads for wire transfer. The implementation classes are org.jboss.remoting.marshal.compress.CompressingMarshaller and org.jboss.remoting.marshal.compress.CompressingUnMarshaller. They extend the org.jboss.remoting.marshal.serializable.SerializableMarshaller and org.jboss.remoting.marshal.serializable.SerializableUnMarshaller interfaces and maintain the same behavior with the addition of compression.

4.6. Callbacks

4.6.1. Callback overview

Although this section covers callback configuration, will need to first cover a little general information about callbacks within remoting. There are two models for callbacks, push and pull. In the push model, the client will register a callback server via an InvokerLocator with the target server. When the target server has a callback to deliver, it will call on the callback server directly and send the callback message.

The other model, pull callbacks, allows the client to call on the target server to collect the callback messages waiting for it. The target server then has to manage these callback messages on the server until the client calls to collect them. Since the server has no control of when the client will call to get the callbacks, it has to be aware of memory constraints as it manages a growing number of callbacks. The way the callback server does this is through use of a persistence policy. This policy indicates at what point the server has too little free memory available and therefore the callback message should be put into a persistent store. This policy can be configured via the memPercentCeiling attribute (see more on configuring this below).

By default, the persistent store used by the invokers is the org.jboss.remoting.NullCallbackStore . The NullCallbackStore will simply throw away the callback to help avoid running out of memory. When the persistence policy is triggered and the NullCallbackStore is called upon to store the callback, the invocation handler making the call will be thrown an IOException with the message:

Callback has been lost because not enough free memory to hold object.

and there will be an error in the log stating which object was lost. In this same scenario, the client will get an instance of the org.jboss.remoting.NullCallbackStore . FailedCallback class when they call to get their callbacks. This class will throw a RuntimeException with the following message when getCallbackObject() is called:

This is an invalid callback. The server ran out of memory, so callbacks were lost.

Also, the payload of the callback will be the same string. The client will also get any valid callbacks that were kept in memory before the persistence policy was triggered.

An example case when using the NullCallbackStore might be when callback objects A, B, and C are stored in memory because there is enough free memory. Then when callback D comes, the persistence policy is triggered and the NullCallbackStore is asked to persist callback D. The NullCallbackStore will throw away callback D and create a FailedCallback object to take its place. Then callback E comes, and there is still too little free memory, so that is thrown away by the NullCallbackStore.

Then the client calls to get its callbacks. It will receive a List containing callbacks A, B, C and the FailedCallback. When the client asks the FailedCallback for its callback payload, it will throw the aforementioned exception.

Besides the default NullCallbackStore, there is a truly persistent CallbackStore, which will persist callback messages to disk so they will not be lost. The description of the CallbackStore is as follows:

Acts as a persistent list which writes Serializable objects to disk and will retrieve them in same order in which they were added (FIFO). Each file will be named according to the current time (using System.currentTimeMillis() with the file suffix specified (see below). When the object is read and returned by calling the getNext() method, the file on disk for that object will be deleted. If for some reason the store VM crashes, the objects will still be available upon next startup. The attributes to make sure to configure are:

file path - this determines which directory to write the objects. The default value is the property value of 'jboss.server.data.dir' and if this is not set, then will be 'data'. For example, might be /jboss/server/default/data.

file suffix - the file suffix to use for the file written for each object stored.

This is also a service mbean, so can be run as a service within JBoss AS or stand alone.

Custom callback stores can also be implemented and defined within configuration. The only requirement is that it implements the org.jboss.remoting.SerializableStore interface and has a void constructor (only in the case of using a fully qualified classname in configuration).

Once a callback client has been removed as a listener, all persisted callbacks will be removed from disk.

4.6.2. Callback Configuration

All callback configuration will need to be defined within the invoker configuration, since the invoker is the parent that creates the callback servers as needed (when client registers for pull callbacks). Example service xml are included below.

callbackMemCeiling - the percentage of free memory available before callbacks will be persisted. If the memory heap allocated has reached its maximum value and the percent of free memory available is less than the callbackMemCeiling, this will trigger persisting of the callback message. The default value is 20.

Note: The calculations for this is not always accurate. The reason is that total memory used is usually less than the max allowed. Thus, the amount of free memory is relative to the total amount allocated at that point in time. It is not until the total amount of memory allocated is equal to the max it will be allowed to allocate. At this point, the amount of free memory becomes relevant. Therefore, if the memory percentage ceiling is high, it might not trigger until after free memory percentage is well below the ceiling.

callbackStore - specifies the callback store to be used. The value can be either an MBean ObjectName or a fully qualified class name. If using class name, the callback store implementation must have a void constructor. The default is to use the NullCallbackStore.

CallbackStore configuration

The CallbackStore can be configured via the invoker configuration as well.

StoreFilePath - indicates to which directory to write the callback objects. The default value is the property value of 'jboss.server.data.dir' and if this is not set, then will be 'data'. Will then append 'remoting' and the callback client's session id. An example would be 'data\remoting\5c4o05l-9jijyx-e5b6xyph-1-e5b6xyph-2'.

StoreFileSuffix - indicates the file suffix to use for the callback objects written to disk. The default value is ‘ser’.

Sample service configuration

Socket transport with callback store specified by class name and memory ceiling set to 30%:

           <mbean code="org.jboss.remoting.transport.Connector"
                 xmbean-dd="org/jboss/remoting/transport/Connector.xml"
                 name="jboss.remoting:service=Connector,transport=Socket"
                 display-name="Socket transport Connector">

              <attribute name="Configuration">
                 <config>
                    <invoker transport="socket">
                       <attribute name="callbackStore">org.jboss.remoting.CallbackStore</attribute>
                       <attribute name="callbackMemCeiling">30</attribute>
                    </invoker>
                    <handlers>
                       <handler subsystem="test">
                          org.jboss.remoting.callback.pull.memory.CallbackInvocationHandler
                       </handler>
                    </handlers>
                 </config>
              </attribute>
           </mbean>
        

Socket transport with callback store specified by MBean ObjectName and declaration of CallbackStore as service:

           <mbean code="org.jboss.remoting.CallbackStore"
                 name="jboss.remoting:service=CallbackStore,type=Serializable"
                 display-name="Persisted Callback Store">

              <!-- the directory to store the persisted callbacks into -->
              <attribute name="StoreFilePath">callback_store</attribute>
              <!-- the file suffix to use for each callback persisted to disk -->
              <attribute name="StoreFileSuffix">cbk</attribute>
           </mbean>

           <mbean code="org.jboss.remoting.transport.Connector"
                 xmbean-dd="org/jboss/remoting/transport/Connector.xml"
                 name="jboss.remoting:service=Connector,transport=Socket"
                 display-name="Socket transport Connector">

              <attribute name="Configuration">
                 <config>
                    <invoker transport="socket">
                       <attribute name="callbackStore">
                          jboss.remoting:service=CallbackStore,type=Serializable
                       </attribute>
                    </invoker>
                    <handlers>
                       <handler subsystem="test">
                          org.jboss.remoting.callback.pull.memory.CallbackInvocationHandler
                       </handler>
                    </handlers>
                 </config>
              </attribute>
           </mbean>
        

Socket transport with callback store specified by class name and the callback store’s file path and file suffix defined:

           <mbean code="org.jboss.remoting.transport.Connector"
                 xmbean-dd="org/jboss/remoting/transport/Connector.xml"
                 name="jboss.remoting:service=Connector,transport=Socket"
                 display-name="Socket transport Connector">

              <attribute name="Configuration">
                 <config>
                    <invoker transport="socket">
                       <attribute name="callbackStore">org.jboss.remoting.CallbackStore</attribute>
                       <attribute name="StoreFilePath">callback</attribute>
                       <attribute name="StoreFileSuffix">cst</attribute>
                    </invoker>
                    <handlers>
                       <handler subsystem="test">
                          org.jboss.remoting.callback.pull.memory.CallbackInvocationHandler
                       </handler>
                    </handlers>
                 </config>
              </attribute>
           </mbean>
        

4.6.3. Callback Exception Handling

Since performing callbacks can sometimes fail, due to network errors or errors produced by the client callback handler, there needs to be a mechanism for managing exceptions when delivering callbacks. This is handled via use of the org.jboss.remoting.callback.CallbackErrorHandler interface. Implementations of this interface can be registered with the Connector to control the behavior when callback exceptions occur.

The implementation of the CallbackErrorHandler interface can be specified by setting the 'callbackErrorHandler' attribute to either the ObjectName of an MBean instance of the CallbackErrorHandler which is already running and registered with the MBeanServer, or can just specify the fully qualified class name of the CallbackErrorHandler implementation (which will be constructed on the fly and must have a void parameter constructor). The full server invoker configuration will be passed along to the CallbackErrorHandler, so if want to add extra configuration information in the invoker's configuration for the callback error handler, it will be available. If no callback error handler is specified via configuration, org.jboss.remoting.callback.DefaultCallbackErrorHandler will be used by default. This implementation will allow up to 5 exceptions to occur when trying to deliver a callback message from the server to the registered callback listener client (regardless of what the cause of the exception is, so could be because could not connect or could be because the client actually threw a valid exception). After the DefaultCallbackErrorHandler receives its fifth exception, it will remove the callback listener from the server invoker handler and shut down the callback listener proxy on the server side. The number of exceptions the DefaultCallbackErrorHandler will allow before removing the listener can by configured by the 'callbackErrorsAllowed' attribute.

4.7. Programmatic configuration

It is possible to configure all this programmatically, if running outside the JBoss Application server for example, but is a little more tedious. Since the remoting components are all bound together by the org.jboss.remoting.transport.Connector class, will need to call its setConfiguration(org.w3c.dom.Element xml) method with same xml as in the mbean service configuration, before calling its start() method.

The xml passed to the Connector should have <config> element as the root element and continue from there with <invoker> sub-element and so on. An example of this can be found in org.jboss.test.remoting.configuration.SocketClientConfigurationTestCase.

4.8. SSL Support and configuration

There are three transports that now support SSL: sslsocket, sslmultiplex, and https. This section will cover configuration, implementation, some samples, and some troubleshooting tips.

Both the sslsocket and https transports are extensions of their non-ssl counterparts, socket and http transports, so the same basic configurations will apply. Therefore, only the ssl specific configurations will be covered here. Moreover, sslmultiplex has the same relationship to multiplex that sslsocket has to socket, so the discussion of sslsocket applies as well to sslmultiplex.

An example of a service xml that covers all the different transport and service configurations can be found within the example-service.xml file under the etc directory of the JBoss Remoting distribution.

sslsocket

The sslsocket transport can be defined in one of two ways if using a service xml to declare the remoting server. The first is to use the sslsocket protocol keyword in the locator url of the InvokerLocator attribute value of the Connector service mbean. For example:

         <mbean code="org.jboss.remoting.transport.Connector"
               xmbean-dd="org/jboss/remoting/transport/Connector.xml"
               name="jboss.remoting:service=Connector,transport=SSLSocket"
               display-name="SSL Socket transport Connector">

            <attribute name="InvokerLocator">sslsocket://myhost:8084</attribute>
      

The other way is to not use the InvokerLocator attribute, but instead a more verbose Configuration attribute, which declares the invoker transport type as a sub-element. For example:

         <mbean code="org.jboss.remoting.transport.Connector"
               xmbean-dd="org/jboss/remoting/transport/Connector.xml"
               name="jboss.remoting:service=Connector,transport=SSLSocket"
               display-name="SSL Socket transport Connector">

            <attribute name="Configuration">
               <config>
                  <invoker transport="sslsocket">
                     <attribute name="numAcceptThreads">1</attribute>
                     <attribute name="maxPoolSize">303</attribute>
         

If defining the remoting server programmatically, not from a server xml file, all that is needed is to create the InvokerLocator with sslsocket as the protocol. Of course the other Connector operations will be needed as well. A simple example would be:

            Connector connector = new Connector();
            InvokerLocator locator = new InvokerLocator(“sslsocket://myhost:8084”);
            connector.setInvokerLocator(locator.getLocatorURI());
            connector.create();
            connector.addInvocationHandler(getSubsystem(), getServerInvocationHandler());
            connector.start();
      

SSL Server Socket Selection

All of the forms of configuration mentioned previously will use the default configuration for selecting which SSL server socket factory to use. Technically, this is done by calling on the javax.net.ssl.SSLServerSocketFactory ’s getDefault() method. This will require that both the javax.net.ssl.keyStore and the javax.net.ssl.keyStorePassword system properties are set. This can be done by either calling the System.setProperty() or via JVM arguments. This also means that all the SSL configurations default to those of the JVM vendor.

There are two ways in which to customize the SSL configuration to be used by the SSLSocketServerInvoker. The first is to explicitly set the server socket factory that the invoker should use to create its server sockets. This can be done programmatically via the following method (which is also exposed as a JMX operation):

public void setServerSocketFactory(ServerSocketFactory serverSocketFactory)

The server socket factory to be used by the invoker can also be set via configuration within the service xml. To do this, the serverSocketFactory attribute will need to be set as a sub-element of the invoker element (this cannot be done if just specifying the invoker configuration using the InvokerLocator attribute). The attribute value must be the JMX ObjectName of an MBean that implements the org.jboss.remoting.security.ServerSocketFactoryMBean interface. An example of this configuration would be:

         <mbean code="org.jboss.remoting.transport.Connector"
               xmbean-dd="org/jboss/remoting/transport/Connector.xml"
               name="jboss.remoting:service=Connector,transport=Socket"
               display-name="Socket transport Connector">

            <attribute name="Configuration">
               <config>
                  <invoker transport="sslsocket">
                     <attribute name="serverSocketFactory">
                        jboss.remoting:service=ServerSocketFactory,type=SSL
                     </attribute>
                     <attribute name="numAcceptThreads">1</attribute>
      

The JBossRemoting project provides an implementation of the ServerSocketFactoryMBean that can be used and should provide most of the customization features that would be needed. More on this implementation later.

The order of selecting which server socket factory is:

1.If a javax.net.ServerSocketFactory has been specified via the setServerSocketFactory() method, use this.

2.If the serverSocketFactory property has been set, then take the String value, create an ObjectName from it, look up that MBean from the MBeanServer that the invoker has been registered with (by way of the Connector) and create a proxy to that MBean of type org.jboss.remoting.security.ServerSocketFactoryMBean. Then use this proxy. Technically, a user could set the serverSocketFactory property with the locator url, but the preferred method is to use the explicit configuration via the invoker element’s attribute, as discussed above.

3.If the server socket factory has not been set explicitly via the serverSocketFactory property, then use the javax.net.ssl.SSLServerSocketFactory ’s getDefault() method.

Note: If want to set the server socket factory via the invoker’s setServerSocketFactory() method, it requires a bit of work, so would opt for using a configuration setting when possible. The following snippet of code shows how it can be done programmatically:

            Connector connector = new Connector();
            InvokerLocator locator = new InvokerLocator(“sslsocket://myhost:8084”);
            connector.setInvokerLocator(locator.getLocatorURI());
            connector.create();
            // create your server socket factory
            ServerSocketFactory svrSocketFactory = createServerSocketFactory();
            // notice that the invoker has to be explicitly cast to the
            // SSLSocketServerInvoker type
            SSLSocketServerInvoker socketSvrInvoker = (SSLSocketServerInvoker) connector.getServerInvoker();
            socketSvrInvoker.setServerSocketFactory(svrSocketFactory);

            connector.addInvocationHandler(getSubsystem(), getServerInvocationHandler());
            connector.start();
         

The ordering is also important in that the call to the Connector’s create() method will create the invoker so that it is available via the getServerInvoker() method. However, the server socket factory MUST be set before the Connector’s start() method is called, because this will cause the invoker’s start() method to be called, which will create the server socket to listen on (and is too late to swap out the server socket factory being used).

https

The https transport is a bit different from the sslsocket in configuration since the implementation is based off the Tomcat connectors. The first major difference is the transport protocol keyword to identify it, which is 'https'.

Next is defining the SSL implementation and server socket factory to be used. The SSL implementation to be used can be set via the 'SSLImplementation' attribute and should always have a value of org.jboss.remoting.transport.coyote.ssl.RemotingSSLImplementation. The server socket factory to be used should be set via the 'serverSocketFactory' attribute and should always be the javax.management.ObjectName value for an implementation of the org.jboss.remoting.security.ServerSocketFactoryMBean interface, which should already be registered with the MBeanServer and running (more details on this in a minute).

An example of setting up https via service.xml configuration would be:

     <mbean code="org.jboss.remoting.transport.Connector"
      xmbean-dd="org/jboss/remoting/transport/Connector.xml"
      name="jboss.remoting:service=Connector,transport=HTTPS"
      display-name="HTTPS transport Connector">

      <attribute name="Configuration">
         <config>
            <invoker transport="https">
               <!-- The following is for setting the server socket factory.  If want ssl support -->
               <!-- use a server socket factory that supports ssl.  The only requirement is that -->
               <!-- the server socket factory value must be an ObjectName, meaning the -->
               <!-- server socket factory implementation must be a MBean and also -->
               <!-- MUST implement the org.jboss.remoting.security.ServerSocketFactoryMBean interface. -->
               <attribute name="serverSocketFactory">jboss.remoting:service=ServerSocketFactory,type=SSL</attribute>
               <attribute name="SSLImplementation">org.jboss.remoting.transport.coyote.ssl.RemotingSSLImplementation</attribute>
               <attribute name="serverBindAddress">${jboss.bind.address}</attribute>
               <attribute name="serverBindPort">6669</attribute>
            </invoker>
            <handlers>
               <handler subsystem="mock">org.jboss.test.remoting.transport.mock.MockServerInvocationHandler</handler>
            </handlers>
         </config>
      </attribute>
      <!-- This depends is included because need to make sure this mbean is running before configure invoker. -->
      <depends>jboss.remoting:service=ServerSocketFactory,type=SSL</depends>
   </mbean>


Notice that the 'serverSocketFactory' attribute has a value of 'jboss.remoting:service=ServerSocketFactory,type=SSL', which is not defined in the configuration snippet. More on how to define this in the next section.

One of the major changes related to using the Tomcat connectors with regards to SSL is that everything within Tomcat is defined via properties configuration and there is no external API for making changes during runtime. This means that there is no way to set server socket factory implementation programatically, other than via configuration (so cannot call setServerSocketFactory() method on the server invoker as could with the ssl socket invoker). This also means that if not running within the JBoss Application Server container, but running stand alone, will need to setup an MBeanServer and register the server socket factory with it programatically. For an example of how to do this programatically, can refer to org.jboss.test.remoting.transport.http.ssl.basic.HTTPSInvokerTestServer.

The configuration for SSL support only works when using the java based http processor and not with the APR based transport. See section 4.7 for more information on using the APR based transport.

SSLSocketBuilder

Although any server socket factory can be set on the SSL socket server invoker and the https server invoker, there is a customizable server socket factory service provided within JBossRemoting that supports SSL. This is the org.jboss.remoting.security.SSLServerSocketFactoryService class. The SSLServerSocketFactoryService class extends the javax.net.ServerSocketFactory class and also implements the SSLServerSocketFactoryServiceMBean interface (so that it can be set using the socketServerFactory attribute described previously). Other than providing the proper interfaces, this class is a simple wrapper around the org.jboss.remoting.security.SSLSocketBuilder class.

The SSLSocketBuilder is where the ssl server socket (and ssl sockets for clients) originate and is where all the properties for the ssl server socket are configured (more on this further below). The SSLSocketBuilder is also a service MBean, so can be configured and started from within a service xml.

This is an example of both the configurations as might be found within a service xml:

            <!-- This service is used to build the SSL Server socket factory -->
            <!-- This will be where all the store/trust information will be set. -->
            <!-- If do not need to make any custom configurations, no extra attributes -->
            <!-- need to be set for the SSLSocketBuilder and just need to set the -->
            <!-- javax.net.ssl.keyStore and javax.net.ssl.keyStorePassword system properties. -->
            <!-- This can be done by just adding something like the following to the run -->
            <!-- script for JBoss -->
            <!-- (this one is for run.bat): -->
            <!-- set JAVA_OPTS=-Djavax.net.ssl.keyStore=.keystore -->
            <!-- -Djavax.net.ssl.keyStorePassword=opensource %JAVA_OPTS% -->
            <!-- Otherwise, if want to customize the attributes for SSLSocketBuilder, -->
            <!-- will need to uncomment them below. -->
            <mbean code="org.jboss.remoting.security.SSLSocketBuilder"
                  name="jboss.remoting:service=SocketBuilder,type=SSL"
                  display-name="SSL Server Socket Factory Builder">
               <!-- IMPORTANT - If making ANY customizations, this MUST be set to false. -->
               <!-- Otherwise, will used default settings and the following attributes will be ignored. -->
               <attribute name="UseSSLServerSocketFactory">false</attribute>
               <!-- This is the url string to the key store to use -->
               <attribute name="KeyStoreURL">.keystore</attribute>
               <!-- The password for the key store -->
               <attribute name="KeyStorePassword">opensource</attribute>
               <!-- The password for the keys (will use KeystorePassword if this is not set explicitly. -->
               <attribute name="KeyPassword">opensource</attribute>
               <!-- The protocol for the SSLContext. Default is TLS. -->
               <attribute name="SecureSocketProtocol">TLS</attribute>
               <!-- The algorithm for the key manager factory. Default is SunX509. -->
               <attribute name="KeyManagementAlgorithm">SunX509</attribute>
               <!-- The type to be used for the key store. -->
               <!-- Defaults to JKS. Some acceptable values are JKS (Java Keystore - Sun's keystore format), -->
               <!-- JCEKS (Java Cryptography Extension keystore - More secure version of JKS), and -->
               <!-- PKCS12 (Public-Key Cryptography Standards #12 keystore - RSA's Personal Information Exchange Syntax Standard). -->
               <!-- These are not case sensitive. -->
               <attribute name="KeyStoreType">JKS</attribute>
            </mbean>

            <!-- The server socket factory mbean to be used as attribute to socket invoker -->
            <!-- See serverSocketFactory attribute above for where it is used -->
            <!-- This service provides the exact same API as the ServerSocketFactory, so -->
            <!-- can be set as an attribute of that type on any MBean requiring an ServerSocketFactory. -->
            <mbean code="org.jboss.remoting.security.SSLServerSocketFactoryService"
                  name="jboss.remoting:service=ServerSocketFactory,type=SSL"
                  display-name="SSL Server Socket Factory">
               <depends optional-attribute-name="SSLSocketBuilder"
                  proxy-type="attribute">jboss.remoting:service=SocketBuilder,type=SSL</depends>
            </mbean>
         

There are two modes in which the SSLSocketBuilder can be run. The first is the default mode where all that is needed is to declare the SSLSocketBuilder and set the system properties javax.net.ssl.keyStore and javax.net.ssl.keyStorePassword. This will use the JVM vendor’s default configuration for creating the SSL server socket factory.

If want to be able to customize any of the SSL properties, the first requirement is that the default mode is turned off. This is IMPORTANT because otherwise, if the default mode is not explicitly turned off, all other settings will be IGNORED, even if they are explicitly set. To turn off the default mode via service xml configuration, set the UseSSLServerSocketFactory attribute to false. This can be done programmatically by calling the setUseSSLServerSocketFactory() and passing false as the parameter value.

The configuration properties are as follows:

SecureSocketProtocol - The protocol for the SSLContext. Some acceptable values are TLS, SSL, and SSLv3. Defaults to TLS (DEFAULT_SECURE_SOCKET_PROTOCOL)

KeyManagementAlgorithm - The algorithm for the key manager factory. Defaults to SunX509 (DEFAULT_KEY_MANAGEMENT_ALGORITHM)

KeyStoreType - The type to be used for the key store. Defaults to JKS (DEFAULT_KEY_STORE_TYPE). Some acceptable values are JKS (Java Keystore - Sun's keystore format), JCEKS (Java Cryptography Extension keystore - More secure version of JKS), and PKCS12 (Public-Key Cryptography Standards #12 keystore - RSA's Personal Information Exchange Syntax Standard). These are not case sensitive.

KeyStorePassword - The password to use for the key store. This only needs to be set if setUseSSLServerSocketFactory() is set to false (otherwise will be ignored). The value passed will also be used for the key password if it is not explicitly set.

KeyPassword - Sets the password to use for the keys within the key store. This only needs to be set if setUseSSLServerSocketFactory() is set to false (otherwise will be ignored). If this value is not set, but the key store password is, it will use that value for the key password.

Some other points of note:

  • A SecureRandom is NOT configurable. When calling SSLContext's init() method, it is actually null, so will use the default implementation.

  • Note that there are currently no ways to specify providers, so will use the default provider (which is determined by the JVM vendor).

  • If the key password is not set, will try to use the value of the key store password.

Configuring SSL sockets for the Client.

There is a simple method for configuring SSL sockets on the client side that applies to all transports. A Map of parameters may be passed to Client with the usual SSL configuration parameters, and it will create an SSLSocketFactory that will be used to generate all sockets for connecting from the client to the server. The keys in the Map may be given by constants in the RemotingSSLSocketFactory class:

REMOTING_ALGORITHM - key store key management algorithm. Defaults to SunX509.

REMOTING_KEY_ALIAS - preferred identity in key store to be used by key managers

REMOTING_KEY_STORE_FILE_PATH - location of key store

REMOTING_KEY_STORE_PASSWORD - key store password

REMOTING_KEY_STORE_TYPE - type of key store. Defaults to JKS.

REMOTING_TRUST_ALGORITHM - trust store key management algorithm. Defaults to SunX509.

REMOTING_TRUST_STORE_FILE_PATH - location of trust store

REMOTING_TRUST_STORE_PASSWORD - trust store password

REMOTING_KEY_STORE_TYPE - type of trust store. Defaults to JKS.

If any of REMOTING_KEY_STORE_FILE_PATH, REMOTING_KEY_STORE_PASSWORD, REMOTING_KEY_STORE_TYPE, REMOTING_TRUST_STORE_FILE_PATH, REMOTING_TRUST_STORE_PASSWORD, REMOTING_KEY_STORE_TYPE are omitted from the configuration Map, RemotingSSLSocketFactory will examine the corresponding standard SSL system properties "javax.net.ssl.keyStore", "javax.net.ssl.keyStorePassword", "javax.net.ssl.keyStoreType", "javax.net.ssl.trustStore", "javax.net.ssl.trustStorePassword", "javax.net.ssl.trustStoreType" instead.

Here is a simple example, drawn from org.jboss.test.remoting.transport.socket.ssl.custom.InvokerClientTest.java:

            Map config = new HashMap();
            config.put(RemotingSSLSocketFactory.REMOTING_TRUST_STORE_TYPE, "JKS");
            String trustStoreFilePath = this.getClass().getResource("../.truststore").getFile();
            config.put(RemotingSSLSocketFactory.REMOTING_TRUST_STORE_FILE_PATH, trustStoreFilePath);
            config.put(RemotingSSLSocketFactory.REMOTING_TRUST_STORE_PASSWORD, "unit-tests-client");
         
            InvokerLocator locator = new InvokerLocator(getTransport() + "://localhost:" + port);
            client = new Client(locator, config);
            client.connect();
      

As always in client-server systems, client and server roles are relative. In the case of push callbacks, the server acts as a client when it establishes a connection over which to transmit the callbacks, and the client acts as a server when it accepts the connection. Accordingly, there needs to be a way to configure the SSLSocket used by the server-side Connector to send callbacks. It works exactly like the configuration mechanism on the client side, but the Map is passed to the Connector() constructor instead of the Client() constructor. Here is an example drawn from org.jboss.test.remoting.transport.socket.ssl.custom.InvokerServerTest.java, which configures both the callback SSLSockets and the SSLServerSocket:

      
            Map config = new HashMap();
            config.put(RemotingSSLSocketFactory.REMOTING_TRUST_STORE_TYPE, "JKS");
            String trustStoreFilePath = this.getClass().getResource("../.truststore").getFile();
            config.put(RemotingSSLSocketFactory.REMOTING_TRUST_STORE_FILE_PATH, trustStoreFilePath);
            config.put(RemotingSSLSocketFactory.REMOTING_TRUST_STORE_PASSWORD, "unit-tests-client");

            Connector connector = new Connector(config);
            InvokerLocator locator = new InvokerLocator(buildLocatorURI(metatdata));
            connector.setInvokerLocator(locator.getLocatorURI());
            connector.create();

            ServerSocketFactory svrSocketFactory = createServerSocketFactory();
            connector.getServerInvoker().setServerSocketFactory(svrSocketFactory);
      
            connector.addInvocationHandler(getSubsystem(), getServerInvocationHandler());
            connector.start();
      

General Security How To

Since we are talking about keystores and truststores, this section will quickly go over how to quickly generate a test keystore and truststore for testing. This is not intended to be a full security overview, just an example of how I originally created mine for testing.

To get started, will need to create key store and trust store.

Generating key entry into keystore:

            C:\tmp\ssl>keytool -genkey -alias remoting -keyalg RSA
            Enter keystore password: opensource
            What is your first and last name?
            [Unknown]: Tom Elrod
            What is the name of your organizational unit?
            [Unknown]: Development
            What is the name of your organization?
            [Unknown]: JBoss Inc
            What is the name of your City or Locality?
            [Unknown]: Atlanta
            What is the name of your State or Province?
            [Unknown]: GA
            What is the two-letter country code for this unit?
            [Unknown]: US
            Is CN=Tom Elrod, OU=Development, O=JBoss Inc, L=Atlanta, ST=GA, C=US correct?
            [no]: yes

            Enter key password for <remoting>
            (RETURN if same as keystore password):
         

Since did not specify the -keystore filename parameter, created the keystore in $HOME/.keystore (or C:\Documents and Settings\Tom\.keystore).

Export the RSA certificate (without the private key)

            C:\tmp\ssl>keytool -export -alias remoting -file remoting.cer
            Enter keystore password: opensource
            Certificate stored in file <remoting.cer>
         

Import the RSE certificate into a new truststore file.

            C:\tmp\ssl>keytool -import -alias remoting -keystore .truststore -file remoting.cer
            Enter keystore password: opensource
            Owner: CN=Tom Elrod, OU=Development, O=JBoss Inc, L=Atlanta, ST=GA, C=US
            Issuer: CN=Tom Elrod, OU=Development, O=JBoss Inc, L=Atlanta, ST=GA, C=US
            Serial number: 426f1ee3
            Valid from: Wed Apr 27 01:10:59 EDT 2005 until: Tue Jul 26 01:10:59 EDT 2005
            Certificate fingerprints:
            MD5: CF:D0:A8:7D:20:49:30:67:44:03:98:5F:8E:01:4A:6A
            SHA1: C6:76:3B:6C:79:3B:8D:FD:FB:4F:33:3B:25:C9:01:9D:50:BF:9F:8A
            Trust this certificate? [no]: yes
            Certificate was added to keystore
         

Now have two files, .keystore for the server and .truststore for the client.

Troubleshooting Tips

Common errors when using server socket factory:

javax.net.ssl.SSLException: No available certificate corresponds to the SSL cipher suites which are enabled.

The 'javax.net.ssl.keyStore' system property has not been set and are using the default SSLServerSocketFactory.

java.net.SocketException: Default SSL context init failed: Cannot recover key

The 'javax.net.ssl.keyStorePassword' system property has not been set and are using the default SSLServerSocketFactory.

java.io.IOException: Can not create SSL Server Socket Factory due to the url to the key store not being set.

The default SSLServerSocketFactory is NOT being used (so custom configuration for the server socket factory) and the key store url has not been set.

java.lang.IllegalArgumentException: password can't be null

The default SSLServerSocketFactory is NOT being used (so custom configuration for the server socket factory) and the key store password has not been set.