JDK-4272406 : Can't define customized RMISocketFactory with Java Plug-in
  • Type: Bug
  • Component: deploy
  • Sub-Component: plugin
  • Affected Version: 1.2.1
  • Priority: P3
  • Status: Closed
  • Resolution: Not an Issue
  • OS: generic
  • CPU: generic
  • Submitted: 1999-09-16
  • Updated: 1999-10-04
  • Resolved: 1999-09-29
Description

Name: clC74495			Date: 09/16/99


1. Steps
 - In client code, set SocketFactory to customized one like this;
---------------------------------
       RMISocketFactory.setSocketFactory(new
           RMISocketFactory);
---------------------------------
 - Start up the client with Java Plug-in
 - look up the RMI server
 - starting communication
 - you will see the error message in the following;

2. Source Code for Client
  * You don't need the code for server to meet the error.
---------------------------------
// SetSocket.java
import java.applet.*;
import java.rmi.server.*;
import java.rmi.Naming;
import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;

public class SetSocket extends Applet implements ActionListener{

	Server server; // remote interface named Server
	TextField textfield;
	Label label;
	Button button;
	
	/** init method */
	public void init(){
	
	// show default SocketFactory
		System.out.println("Defined Socket Factory->"+RMISocketFactory.getSocketFactory());

		// set RMISocketFactory to customized one
		RMISocketFactory sf = new MyRMISocketFactory();
		try{
			RMISocketFactory.setSocketFactory(sf);
			System.out.println("Set Socket Factory done ...\n"
				+ "Defined Socket Factory->"
				+ RMISocketFactory.getSocketFactory());
		} catch (Exception ex) {
			System.out.println("Set Scket Factory: an exception occurred:");
			ex.printStackTrace();
			System.out.println("Defined Socket Factory(err)->"+RMISocketFactory.getSocketFactory());
		}
		
		// call a method which is specially added to MyRMISocketFactory
		// You'll meet an exception if you have failed to set SocketFactory
		((MyRMISocketFactory)RMISocketFactory.getSocketFactory()).addedMethod();
		
		try{
			server = (Server)Naming.lookup("//"+
				getCodeBase().getHost()+"/RMIServer");
			System.out.println("Lookup is successfully completed");
		}catch(Exception re){
			re.printStackTrace();
		}
		draw();
	}
	
	public void draw(){
		
		// show components to confirm server response
		textfield= new TextField("input_string");
		label= new Label("still_blank_label");
		label.setBackground(Color.blue);
		button = new Button("push!");
		button.addActionListener(this);
		
		add(label);
		add(button);
		add(textfield);
	}
	
	// send a String to the server and receive changed String by server
	public void actionPerformed(ActionEvent ae){
		String rmiSt=null;
		String st=textfield.getText();
		try{
			rmiSt = server.reply(st);
			System.out.println("returned String is :"+rmiSt);
		}catch(Exception rex){
			rex.printStackTrace();
		}
		label.setText(rmiSt);
		repaint();
	}
---------------------------------
// MyRMISocketFactory.java
import java.net.*;
import java.io.*;

public class MyRMISocketFactory extends java.rmi.server.RMISocketFactory{
	public Socket createSocket(String host,int port) throws IOException {
		return new Socket(host, port);
	}
	public ServerSocket createServerSocket(int port) throws IOException {
		return new ServerSocket(port);
	}
	public void addedMethod(){
		System.out.println("This RMISocketFactory is successfully set to customized one!!");
	}
}
---------------------------------

3. error message
--------------------------------------------------
Set Socket Factory: an exception occurred:
java.net.SocketException: factory already defined
	at java.rmi.server.RMISocketFactory.setSocketFactory(RMISocketFactory.java:100)
	at SetSocket.init(SetSocket.java:26)
	at sun.applet.AppletPanel.run(AppletPanel.java:357)
	at java.lang.Thread.run(Thread.java:479)
Defined Socket Factory(err)->sun.plugin.RMIPluginSocketFactory@4effdbe7
--------------------------------------------------
4. Any information
 - The contents of our RMISocketFactory is not concerned  with this error. You can see our MyRMISocketFactory class is really simple. It just extends RMISocketFactory class.

5. additional information
 - We think Java Plug-in itself has already set SocketFactory for corresponding to WWW proxy.
(Review ID: 95320) 
======================================================================

Comments
WORK AROUND Name: clC74495 Date: 09/16/99 - Use appletviewer - Turn off "use WWW proxy" Both of them are work around. But it's not available for commercial users in fact. Many users use WWW proxy. ======================================================================
11-06-2004

SUGGESTED FIX My colleague, Shoji Ishida made the codes to fix this bug. I'll attach them. These codes can solve the bug partially. By using these codes, NTTcom's MyRMISocketFactory() class doesn't display the error message and works correctly. But there remains two big problems. Shoji added one property to fix this bug, but this property is reffered only once when Plug-in is initialized, and can't be changed dinamically after that. For that reason, 1) It is necessary to restart the browser to apply user's setting. 2) If another applet which doesn't set RMIFactory uses RMI, it fails RMI communication, since it can't access the proxy server. As a result, TeaPot can't be used with another applets simultaneously. We will ask NTTcom how serious these problems are for their product. Shoji thinks the changes of RMI's specification is needed to solve these problems. To put it concretely, the following changes seem to be needed: 1) make it possible for RMIFactory's setting to override 2) enable RMIFactory's setting for every ClassLoader
11-06-2004

PUBLIC COMMENTS Add the suggested fix
10-06-2004

EVALUATION Plug-in is setting an RMISocketFactory indirectly by adding HttpToPort and HttpToCGI connection mechanisms to the RMIMasterSocketFactory. I'm investigating whether there is any way to accomodate this request without losing these connection mechanisms. thomas.ball@Eng 1999-09-23 RMI does not support setting the global socket factory from an applet via the java.rmi.server.RMISocketFactory.setSocketFactory method. In any case, the applet would need to be granted the RuntimePermission("setFactory") in order to be able to set the socket factory, so the approach you mention would likely fail in a SecurityException anyway. RMI does not allow the setting of the global socket factory without setFactory permission since it can open up a security hole (as you also mention below). To enable using custom socket factories in applets (as well as enabling more flexible customized socket factory usage), we added (in 1.2) the capability of specifying custom socket factories on a per-remote-object basis. This approach is preferred over setting the global RMISocketFactory. More precisely, a remote object can be exported specifying the custom client and server socket factory pair that will be used in communication with that remote object. The client socket factory code can be downloaded and used within the context of an applet (or application) that communicates with the specific remote object. The relevant APIs (in java.rmi.server.UnicastRemoteObject) are: /** * Create and export a new UnicastRemoteObject object using the * particular supplied port and socket factories * @param port the port number on which the remote object receives calls * (if <code>port</code> is zero, an anonymous port is chosen) * @param csf the client-side socket factory for making calls to the * remote object * @param ssf the server-side socket factory for receiving remote calls * @since 1.2 */ protected UnicastRemoteObject(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException; /** * Export the remote object to make it available to receive incoming calls, * using a transport specified by the given socket factory. * @param obj the remote object to be exported * @param port the port to export the object on * @param csf the client-side socket factory for making calls to the * remote object * @param ssf the server-side socket factory for receiving remote calls * @exception RemoteException if export fails * @since 1.2 */ public static Remote exportObject(Remote obj, int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException; We also added similar APIs (in java.rmi.activation.Activatable) that support customized socket factories for use with activatable objects. Here is a tutorial available on how to use custom socket factories per remote object: http://java.sun.com/products/jdk/1.2/docs/guide/rmi/rmisocketfactory.doc.html As you also mention, the solution you suggest does not handle the non-applet case. It is an open question as to how an "AppContext" is defined in the context of an application that may have multiple executing "work contexts". The new RMI APIs added in 1.2 are sufficiently general to handle both custom client/server communication within an applet as well as from standalone applications. (Evaluation by Ann Wollrath, RMI lead) thomas.ball@Eng 1999-09-29
29-09-1999