テストと開発用の非常に基本的なテキスト インターフェイスを使用して、プロジェクト用の単純なクライアントとサーバーを構築しました (以下の使用方法を示すコード)。
クライアントライブラリにはacceptMessage()メソッドとgetMessage()メソッドがあり、基本的に入力と出力のためにblockingQueueにメッセージをプッシュまたはプルします(クライアントではこれはput()とtake()呼び出しで実装されています) . 1 つのスレッドは System.in でブロックし、acceptMessage を介して読み取り行をクライアントに送信します。他のスレッドは、クライアントの getMessage() メソッドでブロックし、到着時にメッセージを System.out にエコーします。すべて非常に基本的ですが、問題なく動作します。
クライアント ライブラリが動作するようになったので、Swing GUI を使用するアプリケーションにそれを統合する方法を見つけようとしています。これまでのところ、Netbeans のインターフェース作成ツールで、テキスト入力ボックスとラベルを備えた単純なフォームを作成するだけで十分です。アイデアは、テキスト入力ボックスが system.in からの読み取りの代わりになり、ラベルが system.out に書き込まれるはずだったものを表示することです。その時点で、Swing で簡単なテスト アプリを複製したことになります。
私の理解では、Swing GUI と直接対話するものはすべて Swing スレッドで実行する必要がありますが、クライアントは独自のスレッドとして実行するように構築されています。GUI から acceptMessage() にメッセージを送信するのはそれほど難しくないと思います (入力ボックスの内容を読み取り、クライアントで acceptMessage() を呼び出す ActionPerformed メソッドをセットアップする必要があると思いますが、私はまだそれを理解しようとしています)が、応答を返す方法がわかりません. スレッド セーフの問題があるため、GUI スレッドでクライアント呼び出し機能を使用できないことはわかっています。また、クライアント ライブラリは、消費するクラスについて何も知らないように記述されています。クライアント インスタンスは、消費クラスに渡されるだけで、acceptMessage() と getMessage() を使用してメッセージを送受信します。
このアーキテクチャを使用して、クライアントを GUI に簡単に統合することは可能ですか? もしそうなら、クライアントからの入力を処理する正しい方法は何ですか? (私が言ったように、Swing のその側面を理解すれば、クライアントへの出力は特に難しくないと思います)。
私が Swing を使用する主な動機は、a) 文書化された Java の GUI ライブラリとしては最も優れているように思われる、b) Netbeans には Swing を操作するためのツールが既にある、c) 締め切りが厳しく、GUI ライブラリを切り替える時間がない、ということです。ゼロから始めます(これは大学のプロジェクト用です)。もっと時間があれば、おそらく他のライブラリを調べていたでしょうが、それらにも独自の癖のセットがあると思います。
import java.io.*;
import java.util.logging.*;
public class TestApp implements Runnable {
private Client <String> client = null;
private UpstreamChannel upstream = null;
private DownstreamChannel downstream = null;
private Thread upstreamThread = null;
private Thread downstreamThread = null;
private boolean ending = false;
private class UpstreamChannel implements Runnable {
private TestApp outer = null;
@Override
public void run () {
Thread.currentThread ().setName ("TestApp.UpstreamChannel");
try (BufferedReader inReader = new BufferedReader (new InputStreamReader (System.in))) {
while (!this.outer.ending) {
this.outer.client.acceptMessage (inReader.readLine ());
}
} catch (IOException ex) {
Logger.getLogger (this.getClass ().getName ()).log (Level.SEVERE, ex.getMessage (), ex);
} finally {
this.outer.ending = true;
this.outer.downstreamThread.interrupt ();
Thread.currentThread ().interrupt ();
return;
}
}
public UpstreamChannel (TestApp app) {
this.outer = app;
}
}
private class DownstreamChannel implements Runnable {
private TestApp outer = null;
@Override
public void run () {
Thread.currentThread ().setName ("TestApp.DownstreamChannel");
try {
while (!this.outer.ending) {
System.out.println (this.outer.client.getMessage ());
}
} catch (InterruptedException ex) {
Logger.getLogger (this.getClass ().getName ()).log (Level.INFO, ex.getMessage (), ex);
} finally {
this.outer.ending = true;
this.outer.upstreamThread.interrupt ();
Thread.currentThread ().interrupt ();
return;
}
}
public DownstreamChannel (TestApp app) {
this.outer = app;
}
}
@Override
public void run () {
if ((null == this.upstreamThread)
&& (null == this.downstreamThread)) {
this.upstreamThread = new Thread (this.upstream);
this.downstreamThread = new Thread (this.downstream);
this.upstreamThread.start ();
this.downstreamThread.start ();
try {
this.upstreamThread.join ();
this.downstreamThread.join ();
} catch (InterruptedException ex) {
Logger.getLogger (this.getClass ().getName ()).log (Level.INFO, ex.getMessage (), ex);
} finally {
this.upstreamThread.interrupt ();
this.downstreamThread.interrupt ();
System.out.println ("Sayonara");
}
}
}
public TestApp (Client <String> client) {
this.upstream = new UpstreamChannel (this);
this.downstream = new DownstreamChannel (this);
this.client = client;
Logger.getLogger (this.getClass ().getName ()).log (Level.INFO, "Class instantiated");
}
}
クライアント アプリを起動するためのコードは次のとおりです。
public static void main (String[] args) throws UnknownHostException, IOException, InterruptedException {
Client <String> clientInstance = new Client ("localhost", 8113);
TestApp app = new TestApp (clientInstance);
Thread clientThread = new Thread (clientInstance);
Thread appThread = new Thread (app);
clientThread.start ();
appThread.start ();
clientThread.join ();
appThread.interrupt ();
System.exit (0);
}
}
編集: getMessage をポーリングし (したがって、メッセージが到着するまでブロックし)、publish() を使用して使用可能にするワーカー スレッドが解決策になると思いましたが、メッセージが「フロアにドロップ」されるリスクがあると思います。メッセージが立て続けに届きます。
SwingWorker reader = new SwingWorker () {
@Override
protected Object doInBackground () throws Exception {
while (!this.isCancelled ()) {
publish (clientInstance.getMessage ());
}
return null;
}
};