corner imagecorner image
IDEPlatformPluginsDocs & SupportCommunityPartners

バイナリデータを渡す Web サービス (パート 5): Swing クライアントの作成

ここでの目標は、すでに作成、配備した Web サービスのクライアントを作成し、そのクライアントに GUI インタフェースを追加することです。このインタフェースには、Web サービスがバイナリデータとして渡すイメージが表示されます。

クライアントの完全版サンプルは、「NetBeans サンプルカタログ」からダウンロードできます。

このチュートリアルのレッスン

このページの内容は NetBeans IDE 6.9-7.1 が対象です
  1. 概要
  2. Web サービスの作成
  3. Web サービスのコーディングおよびテスト
  4. バイナリデータを渡すためのスキーマと WSDL ファイルの変更
  5. => Swing クライアントの作成

このレッスンの目次

  1. クライアントアプリケーションの作成

  2. JFrame のデザイン
  3. JFrame コンポーネントのバインド

  4. 主クラスのコーディング

クライアントアプリケーションの作成

この節では、Web アプリケーションを作成します。このアプリケーション内で、前のチュートリアルで作成および変更した Web サービスを使用するクライアントを作成します。

クライアントアプリケーションを作成するには、次の手順に従います。

  1. 「ファイル」>「新規プロジェクト」(Linux および Windows では Ctrl-Shift-N、MacOS では ⌘-Shift-N) を選択します。「新規プロジェクト」ウィザードが表示されます。
  2. 「Java」カテゴリから「Java アプリケーション」を選択します。「次へ」をクリックします。「新規 Java アプリケーション」ウィザードが表示されます。「プロジェクト名」に「FlowerClient」と入力します。プロジェクトの場所を選択し、「完了」をクリックします。IDE により新しい Java アプリケーションプロジェクトが作成されます。
  3. FlowerClient」プロジェクトノードを右クリックし、コンテキストメニューから「新規」>「Web サービスクライアント」を選択します。「新規 Web サービスクライアント」ウィザードが開きます。
  4. 「WSDL URL」ラジオボタンを選択し、WSDL ファイルの URL をそのフィールドにペーストします。デフォルトでは、URL は http://localhost:8080/FlowerAlbumService/FlowerServiceService?wsdl です。URL を見つけるには、Web サービスをテストし、ブラウザに表示される URL の末尾を ?Tester から ?wsdl に置き換えます。空のパッケージ名を含め、ほかのデフォルト値をすべて受け入れます。
    WSDL の URL が表示された「新規 Web サービスクライアント」ウィザード
  5. 「完了」をクリックします。IDE により WSDL ファイルがダウンロードされ、Web サービスと対話するためのクライアントスタブが追加され、Java アプリケーションプロジェクトの「プロジェクト」ウィンドウにノードが追加されます。
    新しい Web サービスクライアントが表示された「プロジェクト」ビュー

JFrame フォームのデザイン

この節では、JFrame を Web アプリケーションに追加し、Swing コンポーネントを使用して JFrame 内に GUI インタフェースをデザインします。最後に、Swing コンポーネントを Web サービスクライアントコードにバインドします。

自分で JFrame をデザインしない場合は、すでにデザインされた JFrame Java ファイルをここからダウンロードできます。

  1. FlowerClient」ノードを右クリックし、「新規」>「JFrame フォーム」を選択します。フレームに「FlowerFrame」という名前を付けます。このフレームを flowerclient パッケージに配置します。
  2. FlowerFrame がエディタに表示されます。パレットが開かれていない場合は、パレットを開きます。下部の境界線をおよそ 3 分の 1 まで拡げます。
    エディタで FlowerFrame の「デザイン」ビューを表示し、パレットを開いた状態
  3. パレットの「Swing コンテナ」セクションから JPanel を FlowerFrame にドラッグします。FlowerFrame 全体を埋めるように拡げます。
    FlowerFrame に JPanel を追加して拡げた状態
  4. 「デザイン」ビューでこのパネルを右クリックします。コンテキストメニューから「変数名を変更...」を選択します。パネルに「gardenFlowersPanel」という名前を付けます。
  5. パレットから JLabel を gardenFlowersPanel の上にドラッグします。ラベルを右クリックし、ラベルの変数名を titleLabel に変更します。「titleLabel」をもう一度右クリックし、「テキストを編集」を選択します。テキストを「Garden Flowers」に変更します。必要に応じて、titleLabel のプロパティーを表示して、目立つフォントに変更します。
  6. 「ボタングループ」を「デザイン」ビューにドラッグします。ボタングループの変数名はデフォルトの buttonGroup1 のままにします。
  7. 4 つの「ラジオボタン」を、titleLabel の真下に横一列に並ぶようにドラッグします。各ボタンのプロパティーで、buttonGroup1 のメンバーとして設定します。これらのボタンのほかのプロパティーは、次のように設定します。
    buttonGroup1 のラジオボタン
    変数名 選択状態 テキスト
    asterRadioButton true Aster
    honeysuckleRadioButton false Honeysuckle
    roseRadioButton false Rose
    sunflowerRadioButton false Sunflower
  8. 「スクロール区画」をラジオボタンの下にドラッグします。左右のスペースのすべて、および上下のスペースのおよそ 3 分の 2 を埋めるようにスクロール区画を拡げます。スクロール区画の変数名を mainScrollPane に変更します。
  9. 「パネル」を mainScrollPane にドラッグします。パネルの変数名を mainPanel に変更します。
  10. 「デザイン」ビューで mainPanel を右クリックし、「レイアウトを設定」>「ボーダーレイアウト」を選択します。
  11. 「ボタン」を mainPanel にドラッグします。mainPanel にボーダーレイアウトが設定されているため、ボタンはパネル全体を埋めるように自動的に拡げられます。ボタンの変数名を mainPictureButton に、ボタンのテキストを「Waiting for picture...」に変更します。
  12. もう 1 つの「スクロール区画」を mainScrollPane の下のスペースにドラッグします。残りの全スペースを埋めるように、このスクロール区画を拡げます。新しいスクロール区画の変数名を thumbnailScrollPane に変更します。
  13. 「パネル」を thumbnailScrollPane にドラッグします。このパネルの変数名を thumbnailPanel に変更します。thumbnailPanel のレイアウトを「グリッドレイアウト」に設定します。
  14. 4 つの「ボタン」を thumbnailPanel にドラッグします。thumbnailPanel にグリッドレイアウトが設定されているため、すべてのボタンが自動的に同じサイズになり、パネル全体を埋めるように拡げられます。これらのボタンのプロパティーは、次のように設定します。 thumbnailPanel のボタン
    変数名 テキスト
    asterButton Waiting...
    honeysuckleButton Waiting...
    roseButton Waiting
    sunflowerButton Waiting...

これで JFrame フォームのデザインは完成です。この段階で、FlowerFrame は次のように表示されるはずです。
イメージの代わりにボタンのテキストが表示された、完了した Flower Frame

JFrame コンポーネントのバインド

この節では、コンストラクタでコンポーネントを初期化し、そのコンポーネントをリスナーにバインドします。リスナーは、花の画像を表示するコードを呼び出します。

コンポーネントの初期化

この節では、FlowerFrame コンストラクタを記述します。

  1. エディタを「ソース」ビューに切り替えます。FlowerFrame クラス本文の先頭と FlowerFrame コンストラクタを探します。
    空の FlowerFrame コンストラクタが表示された「ソース」ビュー
  2. コンストラクタの前にある FlowerFrame のクラス本文の先頭に、すべての花の名前の文字列配列を作成します。
    protected static final String[] FLOWERS = {"aster", "honeysuckle", "rose", "sunflower"};
  3. FLOWERS 文字列配列とコンストラクタの間に、flowers という名前の java.util.Map を初期化する行を追加します。このマップは String を取って Image にマップします。
    private Map<String, Image> flowers;
  4. java.util.Mapjava.awt.Image のインポート文を追加します。
  5. flowers マップの特定のインスタンスで Image を特定の String に関連付けるコードを、FlowerFrame コンストラクタに追加します。
    public FlowerFrame(Map<String, Image> flowers) {
    
        this.flowers = flowers;
        for (String flower:FLOWERS) {
            flowers.put(flower,null);
        }
    
        initComponents();    
    } 
  6. ラジオボタン用の 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);
    }
  7. java.awt.event.ItemListenerjava.awt.event.ActionListener のインポート文を追加します。

これでコンストラクタは完成しました。ただし、コードに RBListener クラスと ButtonListener クラスが含まれていないため、コンパイルエラーの警告が表示されます。この 2 つのクラスは、それぞれ ItemListenerActionListener のカスタム実装です。この 2 つのクラスの記述は、次の節で行います。

花の表示

この節では、ラジオボタンと花のボタンのカスタムリスナーを記述します。また、ボタンによって選択された花を判断し、その花の Imageflowers マップから取得するメソッドを記述します。最後に、Main クラスによって呼び出され、各サムネイルの Image を取得するメソッドを記述します。

  1. FlowerFrame のクラス本文で public static void main(String args[]) メソッドを探します。このメソッドとその説明を削除します。このアプリケーションでは、Main クラスを代わりに使用します。
  2. main メソッドの代わりに、ItemListener をラジオボタン用に記述します。このリスナーは、ラジオボタンが選択されたときに新しい花の画像を表示します。
    private class RBListener implements ItemListener {
        public void itemStateChanged(ItemEvent e) {
            showFlower();
        }
    }
  3. java.awt.event.ItemEvent のインポート文を追加します。
  4. カスタム 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);
        }
    }
  5. java.awt.event.ActionEvent のインポート文を追加します。
  6. カスタム ActionListener の下に showFlower メソッドを記述します。このメソッドは、選択されているラジオボタンを判断し、対応する花の Imageflowers マップから取得します。
    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("");
    }
  7. javax.swing.ImageIcon のインポート文を追加します。
  8. 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("");
        }
                }
  9. 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 サービスのオペレーションの呼び出しを実行できるようにします。

  1. Main.java クラスをエディタで開きます。
    空の主クラス
  2. クラス本文の main メソッドの前で、ダウンロードした写真の数を示す int 変数を初期化します。
     private static int downloadedPictures;
  3. 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);
  4. java.awt.Imagejava.util.Map、および java.util.HashMap のインポート文を追加します。
  5. main メソッドの本文で、FlowerFrame を表示するコードを追加します。
    // FlowerFrame を表示する。
    final FlowerFrame frame = new FlowerFrame(flowers);
    frame.setVisible(true);  
  6. main メソッドの本文で、クライアントをサービスに接続するコードを追加します。
    // クライアントはこのコードでサービスに接続する。
    FlowerServiceService service = new FlowerServiceService();
    final FlowerService port = service.getFlowerServicePort();
  7. org.flower.service.FlowerServiceorg.flower.service.FlowerServiceService のインポート文を追加します。
  8. 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();
    }
  9. org.flower.service.IOException_Exception のインポート文を追加します。
  10. 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();
    
  11. 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 プロジェクトの生成物を削除し、プロジェクトを構築してから、もう一度実行します。メインフレームに表示される画像を変更するには、ラジオボタンを選択するか、サムネイルをクリックします。

メーリングリストに登録することによって、NetBeans IDE Java EE 開発機能に関するご意見やご提案を送信したり、サポートを受けたり、最新の開発情報を入手したりできます。