バイナリデータを渡す Web サービス (パート 5): Swing クライアントの作成
ここでの目標は、すでに作成、配備した Web サービスのクライアントを作成し、そのクライアントに GUI インタフェースを追加することです。このインタフェースには、Web サービスがバイナリデータとして渡すイメージが表示されます。
クライアントの完全版サンプルは、「NetBeans サンプルカタログ 」からダウンロードできます。
このチュートリアルのレッスン
概要
Web サービスの作成
Web サービスのコーディングおよびテスト
バイナリデータを渡すためのスキーマと WSDL ファイルの変更
=> Swing クライアントの作成
このレッスンの目次
クライアントアプリケーションの作成
JFrame のデザイン
JFrame コンポーネントのバインド
主クラスのコーディング
クライアントアプリケーションの作成
この節では、Web アプリケーションを作成します。このアプリケーション内で、前のチュートリアルで作成および変更した Web サービスを使用するクライアントを作成します。
クライアントアプリケーションを作成するには、次の手順に従います。
「ファイル」>「新規プロジェクト」(Linux および Windows では Ctrl-Shift-N、MacOS では ⌘-Shift-N) を選択します。「新規プロジェクト」ウィザードが表示されます。
「Java」カテゴリから「Java アプリケーション」を選択します。「次へ」をクリックします。「新規 Java アプリケーション」ウィザードが表示されます。「プロジェクト名」に「FlowerClient 」と入力します。プロジェクトの場所を選択し、「完了」をクリックします。IDE により新しい Java アプリケーションプロジェクトが作成されます。
「FlowerClient 」プロジェクトノードを右クリックし、コンテキストメニューから「新規」>「Web サービスクライアント」を選択します。「新規 Web サービスクライアント」ウィザードが開きます。
「WSDL URL」ラジオボタンを選択し、WSDL ファイルの URL をそのフィールドにペーストします。デフォルトでは、URL は http://localhost:8080/FlowerAlbumService/FlowerServiceService?wsdl です。URL を見つけるには、Web サービスをテストし、ブラウザに表示される URL の末尾を ?Tester から ?wsdl に置き換えます。空のパッケージ名を含め、ほかのデフォルト値をすべて受け入れます。
「完了」をクリックします。IDE により WSDL ファイルがダウンロードされ、Web サービスと対話するためのクライアントスタブが追加され、Java アプリケーションプロジェクトの「プロジェクト」ウィンドウにノードが追加されます。
JFrame フォームのデザイン
この節では、JFrame を Web アプリケーションに追加し、Swing コンポーネントを使用して JFrame 内に GUI インタフェースをデザインします。最後に、Swing コンポーネントを Web サービスクライアントコードにバインドします。
自分で JFrame をデザインしない場合は、すでにデザインされた JFrame Java ファイルをここから ダウンロードできます。
「FlowerClient 」ノードを右クリックし、「新規」>「JFrame フォーム」を選択します。フレームに「FlowerFrame 」という名前を付けます。このフレームを flowerclient パッケージに配置します。
FlowerFrame がエディタに表示されます。パレットが開かれていない場合は、パレットを開きます。下部の境界線をおよそ 3 分の 1 まで拡げます。
パレットの「Swing コンテナ」セクションから JPanel を FlowerFrame にドラッグします。FlowerFrame 全体を埋めるように拡げます。
「デザイン」ビューでこのパネルを右クリックします。コンテキストメニューから「変数名を変更...」を選択します。パネルに「gardenFlowersPanel 」という名前を付けます。
パレットから JLabel を gardenFlowersPanel の上にドラッグします。ラベルを右クリックし、ラベルの変数名を titleLabel に変更します。「titleLabel 」をもう一度右クリックし、「テキストを編集」を選択します。テキストを「Garden Flowers」に変更します。必要に応じて、titleLabel のプロパティーを表示して、目立つフォントに変更します。
「ボタングループ」を「デザイン」ビューにドラッグします。ボタングループの変数名はデフォルトの buttonGroup1 のままにします。
4 つの「ラジオボタン」を、titleLabel の真下に横一列に並ぶようにドラッグします。各ボタンのプロパティーで、buttonGroup1 のメンバーとして設定します。これらのボタンのほかのプロパティーは、次のように設定します。
buttonGroup1 のラジオボタン
asterRadioButton
true
Aster
honeysuckleRadioButton
false
Honeysuckle
roseRadioButton
false
Rose
sunflowerRadioButton
false
Sunflower
「スクロール区画」をラジオボタンの下にドラッグします。左右のスペースのすべて、および上下のスペースのおよそ 3 分の 2 を埋めるようにスクロール区画を拡げます。スクロール区画の変数名を mainScrollPane に変更します。
「パネル」を mainScrollPane にドラッグします。パネルの変数名を mainPanel に変更します。
「デザイン」ビューで mainPanel を右クリックし、「レイアウトを設定」>「ボーダーレイアウト」を選択します。
「ボタン」を mainPanel にドラッグします。mainPanel にボーダーレイアウトが設定されているため、ボタンはパネル全体を埋めるように自動的に拡げられます。ボタンの変数名を mainPictureButton に、ボタンのテキストを「Waiting for picture...」に変更します。
もう 1 つの「スクロール区画」を mainScrollPane の下のスペースにドラッグします。残りの全スペースを埋めるように、このスクロール区画を拡げます。新しいスクロール区画の変数名を thumbnailScrollPane に変更します。
「パネル」を thumbnailScrollPane にドラッグします。このパネルの変数名を thumbnailPanel に変更します。thumbnailPanel のレイアウトを「グリッドレイアウト」に設定します。
4 つの「ボタン」を thumbnailPanel にドラッグします。thumbnailPanel にグリッドレイアウトが設定されているため、すべてのボタンが自動的に同じサイズになり、パネル全体を埋めるように拡げられます。これらのボタンのプロパティーは、次のように設定します。
thumbnailPanel のボタン
asterButton
Waiting...
honeysuckleButton
Waiting...
roseButton
Waiting
sunflowerButton
Waiting...
これで JFrame フォームのデザインは完成です。この段階で、FlowerFrame は次のように表示されるはずです。
この節では、コンストラクタでコンポーネントを初期化し、そのコンポーネントをリスナーにバインドします。リスナーは、花の画像を表示するコードを呼び出します。
この節では、FlowerFrame コンストラクタを記述します。
エディタを「ソース」ビューに切り替えます。FlowerFrame クラス本文の先頭と FlowerFrame コンストラクタを探します。
コンストラクタの前にある FlowerFrame のクラス本文の先頭に、すべての花の名前の文字列配列を作成します。
protected static final String[] FLOWERS = {"aster", "honeysuckle", "rose", "sunflower"};
FLOWERS 文字列配列とコンストラクタの間に、flowers という名前の java.util.Map を初期化する行を追加します。このマップは String を取って Image にマップします。
private Map<String, Image> flowers;
java.util.Map と java.awt.Image のインポート文を追加します。
flowers マップの特定のインスタンスで Image を特定の String に関連付けるコードを、FlowerFrame コンストラクタに追加します。
public FlowerFrame(Map<String, Image> flowers) {
this.flowers = flowers;
for (String flower:FLOWERS) {
flowers.put(flower,null);
}
initComponents();
}
ラジオボタン用の ItemListener と 4 つの花のボタン用の ActionListener を初期化し、デフォルトのタイトルを設定します。
public FlowerFrame(Map<String, Image> flowers) {
this.flowers = flowers;
for (String flower:FLOWERS) {
flowers.put(flower,null);
}
initComponents();
setTitle("Garden Flowers [waiting for picture]");
ItemListener rbListener = new RBListener();
asterRadioButton.addItemListener(rbListener);
honeysuckleRadioButton.addItemListener(rbListener);
roseRadioButton.addItemListener(rbListener);
sunflowerRadioButton.addItemListener(rbListener);
ActionListener bListener = new ButtonListener();
asterButton.addActionListener(bListener);
honeysuckleButton.addActionListener(bListener);
roseButton.addActionListener(bListener);
sunflowerButton.addActionListener(bListener);
}
java.awt.event.ItemListener と java.awt.event.ActionListener のインポート文を追加します。
これでコンストラクタは完成しました。ただし、コードに RBListener クラスと ButtonListener クラスが含まれていないため、コンパイルエラーの警告が表示されます。この 2 つのクラスは、それぞれ ItemListener と ActionListener のカスタム実装です。この 2 つのクラスの記述は、次の節で行います。
この節では、ラジオボタンと花のボタンのカスタムリスナーを記述します。また、ボタンによって選択された花を判断し、その花の Image を flowers マップから取得するメソッドを記述します。最後に、Main クラスによって呼び出され、各サムネイルの Image を取得するメソッドを記述します。
FlowerFrame のクラス本文で public static void main(String args[]) メソッドを探します。このメソッドとその説明を削除します。このアプリケーションでは、Main クラスを代わりに使用します。
main メソッドの代わりに、ItemListener をラジオボタン用に記述します。このリスナーは、ラジオボタンが選択されたときに新しい花の画像を表示します。
private class RBListener implements ItemListener {
public void itemStateChanged(ItemEvent e) {
showFlower();
}
}
java.awt.event.ItemEvent のインポート文を追加します。
カスタム ItemListener の下に、カスタム ActionListener を 4 つの花のボタン用に記述します。ボタンがクリックされると、リスナーは関連するラジオボタンを次のように選択します。
private class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == asterButton) asterRadioButton.setSelected(true);
else if (e.getSource() == honeysuckleButton) honeysuckleRadioButton.setSelected(true);
else if (e.getSource() == roseButton) roseRadioButton.setSelected(true);
else if (e.getSource() == sunflowerButton) sunflowerRadioButton.setSelected(true);
}
}
java.awt.event.ActionEvent のインポート文を追加します。
カスタム ActionListener の下に showFlower メソッドを記述します。このメソッドは、選択されているラジオボタンを判断し、対応する花の Image を flowers マップから取得します。
void showFlower() {
Image img = null;
if (asterRadioButton.isSelected()) {
img = flowers.get("aster");
if (img != null) {
mainPictureButton.setIcon(new ImageIcon(img));
setTitle("Garden Flowers [Aster]");
}
} else if (honeysuckleRadioButton.isSelected()) {
img = flowers.get("honeysuckle");
if (img != null) {
mainPictureButton.setIcon(new ImageIcon(img));
setTitle("Garden Flowers [Honeysuckle]");
}
} else if (roseRadioButton.isSelected()) {
img = flowers.get("rose");
if (img != null) {
mainPictureButton.setIcon(new ImageIcon(img));
setTitle("Garden Flowers [Rose]");
}
} else if (sunflowerRadioButton.isSelected()) {
img = flowers.get("sunflower");
if (img != null) {
mainPictureButton.setIcon(new ImageIcon(img));
setTitle("Garden Flowers [Sunflower]");
}
}
if (img == null) {
mainPictureButton.setIcon(null);
setTitle("Garden Flowers [waiting for picture]");
} else mainPictureButton.setText("");
}
javax.swing.ImageIcon のインポート文を追加します。
setThumbnails メソッドを記述します。このメソッドは、各サムネイルの画像を flowers マップから取得します。Main クラスがこのメソッドを呼び出します。
void setThumbnails(Map<String, Image> thumbs) {
Image img = thumbs.get("aster");
if (img != null) {
asterButton.setIcon(new ImageIcon(img));
asterButton.setText("");
}
img = thumbs.get("honeysuckle");
if (img != null) {
honeysuckleButton.setIcon(new ImageIcon(img));
honeysuckleButton.setText("");
}
img = thumbs.get("rose");
if (img != null) {
roseButton.setIcon(new ImageIcon(img));
roseButton.setText("");
}
img = thumbs.get("sunflower");
if (img != null) {
sunflowerButton.setIcon(new ImageIcon(img));
sunflowerButton.setText("");
}
}
FlowerFrame のコードにペーストしたときにインポートを修正していない場合は、インポートを修正します。エディタで右クリックし、コンテキストメニューから「インポートを修正」を選択すると、すべてのインポートを一度に修正できます。完成した一連のインポート文は次のようになります。
import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.Map; import javax.swing.ImageIcon;
これで FlowerFrame は完成しました。
この節では、Main クラスを完成させ、FlowerFrame の表示、Web サービスへの接続、および Web サービスのオペレーションの呼び出しを実行できるようにします。
Main.java クラスをエディタで開きます。
クラス本文の main メソッドの前で、ダウンロードした写真の数を示す int 変数を初期化します。
private static int downloadedPictures;
main メソッドの本文で、4 つの花用の HashMap と、4 つのサムネイル用に別のHashMap を作成します。final Map<String,Image> flowers = new HashMap<String,Image>(4);
final Map<String,Image> thumbs = new HashMap<String,Image>(4);
java.awt.Image 、java.util.Map 、および java.util.HashMap のインポート文を追加します。
main メソッドの本文で、FlowerFrame を表示するコードを追加します。// FlowerFrame を表示する。
final FlowerFrame frame = new FlowerFrame(flowers);
frame.setVisible(true);
main メソッドの本文で、クライアントをサービスに接続するコードを追加します。// クライアントはこのコードでサービスに接続する。
FlowerServiceService service = new FlowerServiceService();
final FlowerService port = service.getFlowerServicePort();
org.flower.service.FlowerService と org.flower.service.FlowerServiceService のインポート文を追加します。
main メソッドの本文で、4 つの Runnable スレッドの配列を作成し、Web サービスの getFlower オペレーションをスレッドごとに一度呼び出すコードを追加します。// Web サービスの getFlower オペレーションが
// スレッドごとに一度、計 4 回呼び出される。
// オペレーションが完了すると図が
// 特定のボタンに表示される。
Runnable[] tasks = new Runnable[4];
for (int i=0; i<4;i++) {
final int index = i;
tasks[i] = new Runnable() {
public void run() {
try {
// Web サービスの getFlower オペレーションを
// 呼び出す。
Image img = port.getFlower(FlowerFrame.FLOWERS[index]);
System.out.println("picture downloaded: "+FlowerFrame.FLOWERS[index]);
// Hashmap に文字列を追加する。
flowers.put(FlowerFrame.FLOWERS[index],img);
// FlowerFrame の showFlower オペレーション
// を呼び出す。
frame.showFlower();
} catch (IOException_Exception ex) {
ex.printStackTrace();
}
downloadedPictures++;
}
};
new Thread(tasks[i]).start();
}
org.flower.service.IOException_Exception のインポート文を追加します。
the main メソッドの本文で、Web サービスの getThumbnails オペレーションを別スレッドで呼び出すコードを追加します。
// 前の 4 つのスレッドが終了した直後に Web サービスの
// getThumbnails オペレーションが別スレッドで呼び出される。
// 画像がダウンロードされるとサムネイルが
// フレームの一番下に表示される。
Runnable thumbsTask = new Runnable() {
public void run() {
try {
while (downloadedPictures < 4) {
try {Thread.sleep(100);} catch (InterruptedException ex) {}
}
// Web サービスの getThumbnails オペレーション
// を呼び出す。
List<Image> images = port.getThumbnails();
System.out.println("thumbs downloaded");
if (images != null && images.size() == 4) {
for (int i=0;i<4;i++) {
thumbs.put(FlowerFrame.FLOWERS[i],images.get(i));
}
frame.setThumbnails(thumbs);
}
} catch (IOException_Exception ex) {
ex.printStackTrace();
}
}
};
new Thread(thumbsTask).start();
Main.java のコードにペーストしたときにインポートを修正していない場合は、インポートを修正します。エディタで右クリックし、コンテキストメニューから「インポートを修正」を選択すると、すべてのインポートを一度に修正できます。インポートする List クラスの選択肢が表示されたら、「java.util.List 」を選択します。完成した一連のインポート文は次のようになります。
import flower.album.FlowerService; import flower.album.FlowerService_Service; import flower.album.IOException_Exception; import java.awt.Image; import java.util.HashMap; import java.util.List; import java.util.Map;
これで Main クラスは完成しました。
public class Main {
private static int downloadedPictures;
public static void main(String[] args) {
final Map<String,Image> flowers = new HashMap<String,Image>(4);
final Map<String,Image> thumbs = new HashMap<String,Image>(4);
// FlowerFrame を表示する。
final FlowerFrame frame = new FlowerFrame(flowers);
frame.setVisible(true);
// クライアントはこのコードでサービスに接続する。
FlowerService_Service service = new FlowerService_Service();
final FlowerService port = service.getFlowerServicePort();
Runnable[] tasks = new Runnable[4];
// Web サービスの getFlower オペレーションが
// スレッドごとに一度、計 4 回呼び出される。
// オペレーションが完了すると図が
// 特定のボタンに表示される。
for (int i=0; i<4;i++) {
final int index = i;
tasks[i] = new Runnable() {
public void run() {
try {
// Web サービスの getFlower オペレーションを
// 呼び出す。
Image img = port.getFlower(FlowerFrame.FLOWERS[index]);
System.out.println("picture downloaded: "+FlowerFrame.FLOWERS[index]);
// Hashmap に文字列を追加する。
flowers.put(FlowerFrame.FLOWERS[index],img);
// FlowerFrame の showFlower オペレーション
// を呼び出す。
frame.showFlower();
} catch (IOException_Exception ex) {
ex.printStackTrace();
}
downloadedPictures++;
}
};
new Thread(tasks[i]).start();
}
// 前の 4 つのスレッドが終了した直後に Web サービスの
// getThumbnails オペレーションが別スレッドで呼び出される。
// 画像がダウンロードされるとサムネイルが
// フレームの一番下に表示される。
Runnable thumbsTask = new Runnable() {
public void run() {
try {
while (downloadedPictures < 4) {
try {Thread.sleep(100);} catch (InterruptedException ex) {}
}
// Web サービスの getThumbnails オペレーション
// を呼び出す。
List<Image> images = port.getThumbnails();
System.out.println("thumbs downloaded");
if (images != null && images.size() == 4) {
for (int i=0;i<4;i++) {
thumbs.put(FlowerFrame.FLOWERS[i],images.get(i));
}
frame.setThumbnails(thumbs);
}
} catch (IOException_Exception ex) {
ex.printStackTrace();
}
}
};
new Thread(thumbsTask).start();
}
}
これでクライアントアプリケーションは完了です。EJB モジュールに委譲してそのイメージを公開する Web サービスと対話するコードを作成しました。クライアントを右クリックし、「実行」を選択します。Swing アプリケーションが起動し、しばらくすると Web サービスから受信されるイメージが表示されます。表示されない画像がある場合は、FlowerService プロジェクトの生成物を削除し、プロジェクトを構築してから、もう一度実行します。メインフレームに表示される画像を変更するには、ラジオボタンを選択するか、サムネイルをクリックします。
nbj2ee
@
netbeans.org
メーリングリスト に登録することによって、NetBeans IDE Java EE 開発機能に関するご意見やご提案を送信したり、サポートを受けたり、最新の開発情報を入手したりできます。