如何自动复制数据到新的RMI线程?
我正在改编一些 rmi 客户端-服务器应用程序。 我写了几件事:
HelloInterface -> A Hello World interface for RMI
Server -> The server app'
Client -> The client app'
没什么特别的,但是...我已经把手放在一个新的 RMISecurityManager 中,它调用 JNI 方法并检查单独用户的权限:
package rmi;
import java.rmi.RMISecurityManager;
import java.io.*;
public class NativeRMISecurityManager extends RMISecurityManager
{
private boolean unix;
protected static ThreadLocal user = new ThreadLocal();
/*
* On interdit l'utilisation du constructeur par defaut
* pour obliger l'utilisation du constructeur avec user.
*/
private NativeRMISecurityManager()
{
super();
}
public NativeRMISecurityManager (final String user)
{
super();
String OS = System.getProperty("os.name").toLowerCase();
unix = (OS.compareTo("windows") != 0); /* Assume that if not
* windows, then "UNIX/POSIX"
*/
/*
* ThreadLocal's user : Each thread is considered
* to have different access rights to the machine
*/
NativeRMISecurityManager.user.set(user);
if (!unix)
{
System.out.println("Systeme : "+OS);
}
}
public void checkRead(String file)
{
super.checkRead(file);
/*
* If we are on a **IX platform we want to check that
* the _user_ has access rights.
*/
if (unix)
{
String str_user = (String)NativeRMISecurityManager.user.get();
if (file == null)
{
throw new SecurityException("file = NULL !!!");
}
if (str_user == null)
{
throw new SecurityException("user = NULL in the ThreadLocal!!!");
}
int ret = c_checkRead(
file,
str_user
);
if (ret != 0)
{
throw new SecurityException("Access error: " + file);
}
}
}
public native int c_checkRead(String file, String user);
}
在 Server 类中,我正在这样做:
String user = "my_user";
System.setSecurityManager(new NativeRMISecurityManager(user));
这个类似乎在服务器的主线程中工作。 现在的问题是当我尝试连接到该服务器类并查找注册表时。 我得到了这个异常:
Exception in thread "RMI TCP Connection(1)-192.168.42.207" java.lang.ExceptionInInitializerError
at sun.rmi.transport.StreamRemoteCall.getInputStream(StreamRemoteCall.java:111)
at sun.rmi.transport.Transport.serviceCall(Transport.java:118)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:466)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:707)
at java.lang.Thread.run(Thread.java:595)
Caused by: java.lang.SecurityException: user = NULL dans le ThreadLocal!!!
at rmi.NativeRMISecurityManager.checkRead(NativeRMISecurityManager.java:62)
at java.io.File.exists(File.java:700)
at java.lang.ClassLoader$3.run(ClassLoader.java:1689)
at java.security.AccessController.doPrivileged(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1686)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1668)
at java.lang.Runtime.loadLibrary0(Runtime.java:822)
at java.lang.System.loadLibrary(System.java:993)
at sun.security.action.LoadLibraryAction.run(LoadLibraryAction.java:50)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.server.MarshalInputStream.<clinit>(MarshalInputStream.java:97)
... 5 more
恕我直言,这意味着(隐式)创建了一个线程并获取 NativeRMISecurityManager 作为其默认的 SecurityManager。
有人对此有什么建议吗?
I am adapting a little rmi client-server application. I have written several things :
HelloInterface -> A Hello World interface for RMI
Server -> The server app'
Client -> The client app'
Nothing special, but... I have put my hands in a new RMISecurityManager, which calls a JNI method and checks the permission for a separate user:
package rmi;
import java.rmi.RMISecurityManager;
import java.io.*;
public class NativeRMISecurityManager extends RMISecurityManager
{
private boolean unix;
protected static ThreadLocal user = new ThreadLocal();
/*
* On interdit l'utilisation du constructeur par defaut
* pour obliger l'utilisation du constructeur avec user.
*/
private NativeRMISecurityManager()
{
super();
}
public NativeRMISecurityManager (final String user)
{
super();
String OS = System.getProperty("os.name").toLowerCase();
unix = (OS.compareTo("windows") != 0); /* Assume that if not
* windows, then "UNIX/POSIX"
*/
/*
* ThreadLocal's user : Each thread is considered
* to have different access rights to the machine
*/
NativeRMISecurityManager.user.set(user);
if (!unix)
{
System.out.println("Systeme : "+OS);
}
}
public void checkRead(String file)
{
super.checkRead(file);
/*
* If we are on a **IX platform we want to check that
* the _user_ has access rights.
*/
if (unix)
{
String str_user = (String)NativeRMISecurityManager.user.get();
if (file == null)
{
throw new SecurityException("file = NULL !!!");
}
if (str_user == null)
{
throw new SecurityException("user = NULL in the ThreadLocal!!!");
}
int ret = c_checkRead(
file,
str_user
);
if (ret != 0)
{
throw new SecurityException("Access error: " + file);
}
}
}
public native int c_checkRead(String file, String user);
}
In the Server class I'm doing that :
String user = "my_user";
System.setSecurityManager(new NativeRMISecurityManager(user));
This class seems to work in the Server's main thread. Now the problem is when I try and connect to that Server class and lookup the Registry.
I get that exception :
Exception in thread "RMI TCP Connection(1)-192.168.42.207" java.lang.ExceptionInInitializerError
at sun.rmi.transport.StreamRemoteCall.getInputStream(StreamRemoteCall.java:111)
at sun.rmi.transport.Transport.serviceCall(Transport.java:118)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:466)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:707)
at java.lang.Thread.run(Thread.java:595)
Caused by: java.lang.SecurityException: user = NULL dans le ThreadLocal!!!
at rmi.NativeRMISecurityManager.checkRead(NativeRMISecurityManager.java:62)
at java.io.File.exists(File.java:700)
at java.lang.ClassLoader$3.run(ClassLoader.java:1689)
at java.security.AccessController.doPrivileged(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1686)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1668)
at java.lang.Runtime.loadLibrary0(Runtime.java:822)
at java.lang.System.loadLibrary(System.java:993)
at sun.security.action.LoadLibraryAction.run(LoadLibraryAction.java:50)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.server.MarshalInputStream.<clinit>(MarshalInputStream.java:97)
... 5 more
IMHO the meaning of this is that a thread is (implicitly) created and gets the NativeRMISecurityManager as its default SecurityManager.
Would somebody have any advice concerning that ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这是事实,尽管这不是您错误的原因; 问题与 ThreadLocal 的使用有关。 ThreadLocal 的关键属性是每个调用线程都有自己的值。 在这种情况下,“用户”由初始化 NativeRMISecurityManager 并将其设置为系统安全管理器(可能是主线程)的线程设置(并为该线程设置)。
然而,其他一些线程(从 RMI 消息处理线程的角度来看)正在调用 checkRead() - 但从未为此线程设置“user”字段! 因此该值返回为空。
除非有某种原因让不同的线程具有不同的值 - 并且我无法从您的示例中辨别出一个值 - 我建议将“用户”字段设置为a)字符串,而不是ThreadLocal和b)非静态。 这应该可以解决您的空值问题。
然而,假设这是一个需求/设计约束,当前体系结构的问题在于,只有实例化 NativeRMISecurityManager 的线程才真正拥有用户 - 所有其他线程都为 null。
在更深入地研究这个问题时,我认为我需要更好地了解您的问题领域,以便提供有关修复的任何有用的建议。 此外,Java 的安全架构并没有什么快速或肮脏的地方。 不过,我将在以下几个假设下尽最大努力:
潜在的实现:
This is true, though it is not the cause of your error; the problem has to do with the use of ThreadLocal. The key property of a ThreadLocal is that every calling thread has its own value. In this case "user" is being set by (and for) the thread that initializes the NativeRMISecurityManager and sets it as the System Security Manager (presumably the main thread).
However, some other thread (by the look of it the RMI message handling thread) is calling checkRead() - but the "user" field was never set for this thread! Thus the value comes back as null.
Unless there is some reason to have different threads have different values - and I cannot discern one from your example - I would recommend making the "user" field a) a String, not a ThreadLocal and b) non-static. That should solve your null value problem.
However, presuming that it is a requirement / design constraint, the trouble with the current architecture is that only the thread that instantiates the NativeRMISecurityManager actually gets to have a user - every other thread gets null.
In delving into this with a little more depth, I think I need a better understanding of your problem domain to offer any helpful suggestions as to a fix. Further, there is nothing quick or dirty about Java's Security Architecture. However I will do my best working under a few assumptions:
Potential implementation:
是的 ! 就是这样。
昨天下午我就考虑过这个问题,并采用了同样的解决方案。 我将在这里为那些对此感到好奇的人发布我的代码。
1) NativeRMI安全管理器
2)C代码(你必须用javah生成.h
(注意:我不会用英语翻译它,因为有很多法语注释)
和C库:
非常感谢Greg Case。这让我感到安慰,因为我们找到了相同的解决方案:)
Yes ! That's it.
I have thought about that yesterday afternoon, and I went on the same solution. I'll post here my code for those who would be curious of that.
1) The NativeRMISecurityManager
2) The C code (you have to generate the .h with javah
(nb: I won't traduce it in english as there are loads of french comments)
And the C library:
Thank you very much Greg Case. This comfort me because we have found the same solution. :)