- PR -

RMI + Windowsサービス

1
投稿者投稿内容
しんい
ベテラン
会議室デビュー日: 2005/09/01
投稿数: 55
投稿日時: 2008-02-07 14:50
お世話になっております。

内容的にwindowsの会議室かもしれませんが、RMIの扱いの可能性があるので、こちらに質問をさせて頂きたいと思います。

RMIサーバを稼働させるに当たり、Windowsサービス化したいと思いました。
sc.exeコマンドを使い、サービスに登録する方法は理解できているつもりです。(サービス自体にはあまり知識はないです。)
下記方法に、問題等があればご教授頂きたく存じます。
ちなみにOSはwindowsXPです。

例えば、D:\にあるServer.jarファイルをRMIサーバのサービスとして起動させたいと考えております。
手順として、
1.batファイル(server.bat)の作成
 CLASSPATHを通す。(set CLASSPATH=D:\Server.jar)
 start rmiregistry を行い、RMIレジストリを起動
 java -jar D:\Server.jar を行いサーバを起動。
 ※ちなみにこのbatファイルは正常に作動する事を確認済みです。
2.scコマンドによるサービス化
 sc create RMIserver binpath= D:\server.bat

以上でサービスの一覧にRMIserverが登録された事は確認しております。
しかし、サービスを開始しても、[SC] StartService FAILED 1053
とエラーが表示され、作動しません。
RMIサーバの場合、何かしないといけない事があるのか、それともサービスに関してすべきことがあるのかが見えていない状態です。

よろしくお願い致します。
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2008-02-07 17:47
こんばんは。

引用:

しんいさんの書き込み (2008-02-07 14:50) より:
しかし、サービスを開始しても、[SC] StartService FAILED 1053
とエラーが表示され、作動しません。
RMIサーバの場合、何かしないといけない事があるのか、それともサービスに関してすべきことがあるのかが見えていない状態です。



エラーの内容は次のようになっています。

 Error 1053: The service did not respond to the start or control request in a timely fashion
 エラー1053:そのサービスは指定時間内に開始要求または制御要求に応答しませんでした。

これは、サービス制御マネージャ(SCM)から、サービスプログラムへ開始要求をしたにもかかわらず、
サービスプログラム側がそれに対して応答を返していない、ということです。

サービスプログラム側では、SCMからの制御要求に対し、その報告を返す必要があります。

Handler(msdn)
引用:

サービスアプリケーションプロセスのメインスレッド内の制御ディスパッチャは、サービス制御マネージャから制御の要求を受け取ると、指定されたサービスに対応する制御ハンドラ関数を呼び出します。制御の要求を処理した後で、制御ハンドラは SetServiceStatus 関数を呼び出して、自らのステータスをサービス制御マネージャへ報告しなければなりません



Windowsサービスアプリケーションとして動作させるためには、要件をみたす必要がありますので
サービスとして登録しただけでは動作しません。
未記入
大ベテラン
会議室デビュー日: 2008/02/07
投稿数: 115
投稿日時: 2008-02-07 18:41
Windows で RMI サービスを作ろうとはおもしろい。簡単なサンプルがあるので、どうぞ。

(1) rmiregistry.exe を使用しなくてもプログラムコードからRMI レジストリを起動できる。

(2) exewrap というツールを使うことで、Windows サービスを作成することができる。

■ Hello.java
コード:
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Hello extends Remote {
	
	String sayHello() throws RemoteException;

}



■ HelloImpl.java
コード:
import java.rmi.Naming;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

public class HelloImpl extends UnicastRemoteObject implements Hello {

	private static boolean isRunning = true;
	private static Hello hello;
	
	protected HelloImpl() throws RemoteException {
	}

	@Override
	public String sayHello() throws RemoteException {
		return "Hello, RMI World!!";
	}
	
	public static void main(String[] args) throws Exception {
		start(args);
	}
	
	public static void start(String[] args) throws Exception {
		if(System.getSecurityManager() == null) {
			System.setSecurityManager(new RMISecurityManager());
		}
		
		LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
		
		hello = new HelloImpl();
		Naming.rebind("Hello", hello);
		
		while(isRunning) {
			try { Thread.sleep(500); } catch(InterruptedException e) {}; 
		}
	}

	public static void stop() throws Exception {
		isRunning = false;
		UnicastRemoteObject.unexportObject(hello, true);
	}
	
}



■ HelloClient.java
コード:
import java.rmi.Naming;
import java.rmi.RMISecurityManager;

public class HelloClient {
	
	public static void main(String[] args) throws Exception {
		System.setSecurityManager(new RMISecurityManager());

		Hello hello = (Hello)Naming.lookup(  "rmi://localhost/Hello");
		
		System.out.println(hello.sayHello());
	}
	
}



■ HelloServer.manifest
コード:
Main-Class: HelloImpl



■ HelloClient.manifest
コード:
Main-Class: HelloClient



■ HelloServer.policy
コード:
grant {
	permission java.security.AllPermission;
};



■ HelloClient.policy
コード:
grant {
	permission java.security.AllPermission;
};



■ コンパイル & サービスインストール & 実行

(コンパイル)
javac Hello.java HelloImpl.java HelloClient.java

(サーバーJAR作成 & EXE作成)
jar cvfm HelloServer.jar HelloServer.manifest HelloImpl.class
exewrap -s HelloServer.jar

(サービスインストール & サービス開始)
HelloServer.exe -install

net start HelloServer

(クライアントJAR作成 & EXE作成)
jar cvfm HelloClient.jar HelloClient.manifest HelloClient.class
exewrap HelloClient.jar

(クライアント実行)
HelloClient.exe

(サービスの停止 & 削除)
net stop HelloServer
HelloServer.exe -remove
未記入
大ベテラン
会議室デビュー日: 2008/02/07
投稿数: 115
投稿日時: 2008-02-07 18:45
補足。

(1) 以下のファイルは同じところに配置する。
Hello.java
HelloImpl.java
HelloClient.java
HelloServer.manifest
HelloClient.manifest
HelloServer.policy
HelloClient.policy

(2) exewrap はここからダウンロードする。

http://www.ne.jp/asahi/web/ryo/exewrap/
しんい
ベテラン
会議室デビュー日: 2005/09/01
投稿数: 55
投稿日時: 2008-02-07 23:31
Tdnr_Symさん、未記入さん、早速のご回答、ありがとうございます。

scコマンドを見つけたときは、これで解決するとは思ったのですが、、、やはりそう簡単にはいきませんね。

未記入さんに教えて頂いた、javaからrmiregistryを立ち上げるというのは、実は一時探したのですが、検索の仕方がまずかったらしく、当時見つかりませんでした。
ですから、お教え頂いて感動です。
とりあえずサービス化の前に現状のRMIを書き換えて、一旦プログラムから立ち上げるのを試そうと思いましたが、、、上手くいきません。

セキュリティポリシーの部分が良く分からない状態です。
内容を記載致しますので、お手数をお掛け致しますが、ご指摘頂きたく存じます。

・policyファイル(all.policy)
grant{
 permission java.security.AllPermission;
};

・RMIサーバ側の問題部分
System.setProperty("java.security.policy", "D:\\all.policy");
LocateRegistry.createRegistry(38925);
Naming.rebind("rmi://192.168.21.45:38925/Server",new Server());

まず、System.setProperty("java.security.policy", "D:\\all.policy");の部分で引っ掛かり、エラーが出てしまいます。下記、その際のスタックです。
java.security.AccessControlException: access denied(java.util.PropertyPermissio
n java.security.policy write)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323)
at java.security.AccessController.checkPermission(AccessController.java:546)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
at java.lang.System.setProperty(System.java:727)
at src.Win_server.start(Win_server.java:40)
at src.Win_server.main(Win_server.java:29)

System.setProperty("java.security.policy", "D:\\all.policy");を削除して実行すると、サーバは稼動しますが、クライアントからの接続が上手くいかない状態になります。
下記、クライアントから接続した際に、サーバのコンソールに出てくるスタックです。
Exception in thread "RMI TCP Connection(idle)" java.security.AccessControlException: access denied (java.net.SocketPermission 192.168.21.45:49961accept,resolve)
 at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323)
 at java.security.AccessController.checkPermission(AccessController.java:546)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
at java.lang.SecurityManager.checkAccept(SecurityManager.java:1157)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.checkAcceptPermission(TCPTransport.java:636)
at sun.rmi.transport.tcp.TCPTransport.checkAcceptPermission(TCPTransport.java:275)
at sun.rmi.transport.Transport$1.run(Transport.java:158)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:885)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
at java.lang.Thread.run(Thread.java:619)

最終的にはpolicyファイルを使う方がセキュリティ面でも良いのであれば、そのほうが好ましいと思っております。
ネットで調べてもセキュリティマネージャの仕組みがいまいち分かっていないのもあり、良く分からない状態です。
宜しくお願い致します。
未記入
大ベテラン
会議室デビュー日: 2008/02/07
投稿数: 115
投稿日時: 2008-02-08 09:21
引用:

セキュリティポリシーの部分が良く分からない状態です。


セキュリティマネージャをインストールする前にポリシーを指定しておかないといけないので、System.setProperty("java.security.policy", ...) をしてから、System.setSecurityManager(new RMISecurityManager()) を実行すること。

というかセキュリティポリシーは実行時に指定したほうが良いのでは? java -Djava.security.policy=all.policy ... といった感じで。
しんい
ベテラン
会議室デビュー日: 2005/09/01
投稿数: 55
投稿日時: 2008-02-09 16:47
未記入さん、早速のご返答ありがとうございました。

batとサービスにすると、細々と違う部分があり、少し苦戦しましたが、やっと思い通りのサービスを稼動させることができました。(知識が乏しかったので、手当たり次第やっていました。)

セキュリティポリシーに関しては、何でもjavaでやりたがる傾向があるので、内包してしまいました。
今回のサービスには、どの道、同じフォルダにpolicyファイルを置くだけで良かったので、プログラムから除外しました。

理想通りの状態になって、感謝しております。
ありがとうございました。
1

スキルアップ/キャリアアップ(JOB@IT)