// 
//     Licensed to the Apache Software Foundation (ASF) under one
//     or more contributor license agreements.  See the NOTICE file
//     distributed with this work for additional information
//     regarding copyright ownership.  The ASF licenses this file
//     to you under the Apache License, Version 2.0 (the
//     "License"); you may not use this file except in compliance
//     with the License.  You may obtain a copy of the License at
// 
//       http://www.apache.org/licenses/LICENSE-2.0
// 
//     Unless required by applicable law or agreed to in writing,
//     software distributed under the License is distributed on an
//     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
//     KIND, either express or implied.  See the License for the
//     specific language governing permissions and limitations
//     under the License.
//

= Передача двоичных данных с помощью приложения веб-службы, часть 5: создание клиента Swing
:jbake-type: tutorial
:jbake-tags: tutorials 
:jbake-status: published
:icons: font
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:description: Передача двоичных данных с помощью приложения веб-службы, часть 5: создание клиента Swing - Apache NetBeans
:keywords: Apache NetBeans, Tutorials, Передача двоичных данных с помощью приложения веб-службы, часть 5: создание клиента Swing

Целью этого упражнения является создание клиента для веб-службы, которая была создана и развернута ранее, с последующим добавлением графического интерфейса пользователя в этот клиент. Интерфейс служит для вывода на экран изображений, переданных веб-службой в качестве двоичных данных.

Полную версию клиента можно загрузить из link:https://netbeans.org/projects/samples/downloads/download/Samples%252FWeb%2520Services%252FWeb%2520Service%2520Passing%2520Binary%2520Data%2520--%2520EE6%252FFlowerClient.zip[+каталога примеров NetBeans+].

*Уроки, представленные в этом учебном курсе*

image::images/netbeans-stamp-74-73-72.png[title="Содержимое этой страницы применимо к IDE NetBeans 7.2, 7.3 и 7.4"]

1. link:./flower_overview.html[+Обзор+]
2. link:./flower_ws.html[+Создание веб-службы+]
3. link:./flower-code-ws.html[+Написание кода веб-службы и ее тестирование+]
4. link:./flower_wsdl_schema.html[+Изменение схемы и файлов WSDL для передачи двоичных данных+]
5. => Создание клиента Swing


1. <<create-client-app,Создание приложения клиента>>

2. <<design-jframe,Проектирование JFrame>>
3. 
<<bind-jframe,Привязка компонентов JFrame>>

4. <<code-main-class,Написание кода главного класса>>


==  Создание клиента приложения

В этом разделе рассматривается создание веб-приложения. В рамках приложения выполняется создание клиента, потребляющего веб-службу, которая была создана и изменена в предыдущих учебных курсах.

*Создание приложения*

1. Выберите 'Файл' > 'Создать проект' (Ctrl-Shift-N в Windows и Linux, ⌘-Shift-N в ОС Mac). Откроется мастер создания проекта.
2. Выберите "Приложение Java" в категории "Java". Нажмите кнопку "Далее". Появится мастер создания приложения Java. Введите текст  ``FlowerClient``  в поле "Имя проекта". Выберите папку проекта и нажмите кнопку "Готово". В среде IDE будет создан новый проект приложения на Java.
3. Щелкните правой кнопкой мыши узел проекта  ``FlowerClient``  и последовательно выберите в контекстном меню команды "Создать" > "Клиент веб-службы". Появится мастер создания клиента веб-службы.
4. Выберите переключатель "URL-адрес WSDL" и вставьте в соответствующее поле URL-адрес файла WSDL. По умолчанию URL-адресом является  ``http://localhost:8080/FlowerAlbumService/FlowerServiceService?wsdl`` . URL-адрес можно найти в браузере, выполнив тестирование веб-службы и заменив текст  ``?Tester``  на  ``?wsdl``  в конце URL-адреса. Примите все прочие значения по умолчанию, включая пустое имя пакета. 
image::images/ws-client-wiz.png[]
5. Нажмите кнопку "Завершить". В среде IDE выполняется загрузка файла WSDL, добавление заглушек клиента для взаимодействия с веб-службой, а также добавление узлов в проект приложения на Java в окне "Проекты". 
image::images/client-generated-sources.png[title="Представление проекта, в котором отображается новый клиент веб-службы"]


[[design-jframe]]
== Разработка формы JFrame

В этом разделе рассматривается добавление формы  ``JFrame``  в веб-приложение и проектирование в рамках этого приложения графического интерфейса пользователя с использованием компонентов Swing. В заключение описывается привязка компонентов Swing к коду клиента веб-службы.

При отсутствии необходимости самостоятельного проектирования формы JFrame можно загрузить готовый файл JFrame Java link:https://netbeans.org/projects/www/downloads/download/webservices%252FFlowerFrame.java[+здесь+].

1. Щелкните правой кнопкой мыши узел  ``FlowerClient``  и выберите пункт "Создать", а затем – "Форма JFrame". Присвойте фрейму имя  ``FlowerFrame`` . Разместите его в пакете  ``flowerclient`` .
2. Фрейм  ``FlowerFrame``  будет открыт в редакторе. Откройте палитру (если она еще не открыта). Растяните нижнюю границу фрейма приблизительно на одну треть.
image::images/opened-flowerform.png[]
3. Перетащите панель JPanel из раздела "Контейнеры Swing" на палитре в фрейм  ``FlowerFrame`` . Разверните панель, чтобы она заполнила все пространство фрейма  ``FlowerFrame`` . 
image::images/add-panel.png[]
4. Щелкните правой кнопкой мыши панель в представлении "Проектирование". Выберите в контекстном меню пункт "Изменить имя переменной...". Присвойте панели имя  ``gardenFlowersPanel`` .
5. Перетащите метку JLabel из палитры в верхнюю область панели  `` gardenFlowersPanel`` . Щелкните метку правой кнопкой мыши и измените имя переменной метки на  ``titleLabel`` . Снова щелкните правой кнопкой мыши метку  ``titleLabel``  и выберите пункт "Изменить текст". Измените текст на "Garden Flowers". Можно также изучить свойства  ``titleLabel``  и увеличить шрифт.
6. Перетащите группу кнопок в представление "Проектирование". Примите имя группы кнопок  ``buttonGroup1`` , заданное по умолчанию.
7. Перетащите четыре переключателя в горизонтальный ряд под меткой  ``titleLabel`` . В свойствах каждого переключателя укажите, что он является компонентом группы кнопок  ``buttonGroup1`` . Для других свойств переключателей действительны следующие свойства:
|===
Переключатели в группе кнопок buttonGroup1

|Имя переменной |Выбрано |Текст 

|asterRadioButton |true |Aster 

|honeysuckleRadioButton |false |Honeysuckle 

|roseRadioButton |false |Rose 

|sunflowerRadioButton |false |Sunflower 
|===
8. Перетащите панель с прокруткой в область под переключателями. Разверните панель, чтобы она заполнила всю область по горизонтали и приблизительно две трети свободной области по вертикали. Измените имя переменной панели с прокруткой на  ``mainScrollPane`` .
9. Перетащите панели на панель  ``mainScrollPane`` . Измените имя переменной панели на  ``mainPanel`` .
10. В представление 'Конструирование' щелкните правой кнопкой мыши  ``mainPanel``  и выберите 'Настроить макет' > 'Макет границ'.
11. Перетащите кнопку на панель  ``mainPanel`` . Поскольку панель  ``mainPanel``  имеет границы, кнопка автоматически заполняет все пространство панели. Измените имя переменной кнопки на  ``mainPictureButton`` , и введите для кнопки текст "Waiting for picture..."
12. Перетащите еще одну панель с прокруткой в область под панелью  ``mainScrollPane`` . Разверните новую панель с прокруткой, чтобы она заполнила все свободное пространство. Измените имя переменной новой панели с прокруткой на  ``thumbnailScrollPane`` .
13. Перетащите панель из палитры на панель  ``thumbnailScrollPane`` . Измените имя переменной панели на  ``thumbnailPanel`` . Установите в качестве формата панели  ``thumbnailPanel``  формат сетки.
14. Перетащите четыре кнопки на панель  ``thumbnailPanel`` . Поскольку панель  ``thumbnailPanel``  имеет формат сетки, кнопки автоматически становятся одинакового размера и полностью заполняют панель. Свойства кнопок Кнопки на панели thumbnailPanel
|===

|Имя переменной |Текст 

|asterButton |Ожидание... 

|honeysuckleButton |Ожидание... 

|roseButton |Ожидание 

|sunflowerButton |Ожидание 
|===

Форма JFrame готова. На данный момент файл  ``FlowerFrame``  выглядит следующим образом. 
image::images/designed-form.png[title="Завершенный фрейм Flower, в которой отображается текст кнопок вместо изображений"]


== Привязка компонентов JFrame

В этом разделе рассматривается инициализация компонентов в конструкторе и привязка компонентов к прослушивателям. Прослушиватели используются для вызова кода, который отвечает за отображение цветов.


=== [[Инициализация компонентов]] 

[[В этом разделе описывается заполнение конструктора  ``FlowerFrame`` 

]]

1. Перейдите в представление "Исходный код" в редакторе. Найдите начало тела класса  ``FlowerFrame``  и конструктор  ``FlowerFrame`` .
image::images/ff-empty-constructor.png[]
1. В верхней части тела класса фрейма  ``FlowerFrame``  создайте над строкой конструктора массив строк с названиями всех цветов.

[source,java]
----

protected static final String[] FLOWERS = {"aster", "honeysuckle", "rose", "sunflower"};
----
1. Добавьте между массивом строк FLOWERS и конструктором строку, инициализирующую элемент  ``link:http://download.oracle.com/javase/6/docs/api/java/util/Map.html[+java.util.Map+]``  с именем ``flowers`` . Карта ссылок принимает объект  ``String``  и устанавливает его соответствие с объектом  ``Image`` .

[source,java]
----

private Map<String, Image> flowers;
----
1. Добавьте операторы импорта для  ``java.util.Map``  и  ``java.awt.Image`` .
2. Добавьте код в конструктор  ``FlowerFrame`` , чтобы связать определенный объект  ``Image``  с определенным объектом  ``String``  для определенного экземпляра карты ссылок  ``flowers`` .

[source,java]
----

public FlowerFrame(Map<String, Image> flowers) {

    this.flowers = flowers;
    for (String flower:FLOWERS) {
        flowers.put(flower,null);
    }

    initComponents();    
} 
----
3. Инициализируйте прослушиватели  ``ItemListener``  для переключателей, а также прослушиватели  ``ActionListener``  для четырех кнопок цветов и установите заголовок по умолчанию.

[source,java]
----

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);
}
----
4. Добавьте операторы импорта для  ``link:http://download.oracle.com/javase/6/docs/api/java/awt/event/ItemListener.html[+java.awt.event.ItemListener+]``  и  ``link:http://download.oracle.com/javase/6/docs/api/java/awt/event/ActionListener.html[+java.awt.event.ActionListener+]`` .

Конструктор готов. В коде выводятся сообщения об ошибках, поскольку код не содержит классы  ``RBListener``  и  ``ButtonListener`` . Эти два класса представляют собой реализации прослушивателей  ``ItemListener``  и  ``ActionListener`` , соответственно. Запись этих классов в код рассматривается в следующем разделе.


=== [[Изображения с цветами]] 

[[В этом разделе рассматривается написание пользовательских прослушивателей для переключателей и кнопок цветов. Также рассматривается написание метода, который определяет цветок, выбираемый при нажатии кнопки, и получает объект  ``Image``  цветка на карте ссылок  ``flowers`` . В завершение описывается создание метода, который вызывается посредством класса  ``Main``  и получает объект  ``Image``  для каждого эскиза.

]]

1. Найдите метод  ``public static void main(String args[])``  в теле класса  ``FlowerFrame`` . Удалите этот метод и его документацию. (Вместо него в приложении будет использован класс  ``Main`` .)
1. Напишите вместо метода  ``main``  пользовательский прослушиватель  ``ItemListener``  для переключателей. Этот прослушиватель служит для отображения нового изображения цветка при выборе переключателя.

[source,java]
----

private class RBListener implements ItemListener {
    public void itemStateChanged(ItemEvent e) {
        showFlower();
    }
}
----
1. Добавьте оператор импорта для  ``link:http://download.oracle.com/javase/6/docs/api/java/awt/event/ItemEvent.html[+java.awt.event.ItemEvent+]`` .
1. Запишите под пользовательским прослушивателем  ``ItemListener``  пользовательский прослушиватель  ``ActionListener``  для четырех кнопок цветов. При нажатии кнопки прослушиватель выбирает соответствующий переключатель:

[source,java]
----

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);
    }
}
----
2. Добавьте оператор импорта для  ``link:http://download.oracle.com/javase/6/docs/api/java/awt/event/ActionEvent.html[+java.awt.event.ActionEvent+]`` .
3. Напишите под пользовательским прослушивателем  ``ActionListener``  метод  ``showFlower`` . Этот метод определяет выбранный переключатель и получает объект  ``Image``  для соответствующего цветка из карты ссылок  ``flowers`` .

[source,java]
----

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("");
}
----
4. Добавьте оператор импорта для  ``link:http://download.oracle.com/javase/6/docs/api/javax/swing/ImageIcon.html[+javax.swing.ImageIcon+]`` .
5. Напишите метод  ``setThumbnails`` . Этот метод получает изображение для каждого эскиза из карты ссылок  ``flowers`` . Метод вызывается посредством класса  ``Main`` .

[source,java]
----

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("");
    }
}
----
6. Исправьте операторы импорта  ``FlowerFrame`` , если они не были исправлены ранее при вставке в код. Можно исправить все ошибки одновременно, щелкнув правой кнопкой мыши в редакторе и выберите 'Исправить выражения импорта' в контекстном меню. Ниже представлен полный список операторов импорта:

[source,java]
----

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]] 

[[В этом разделе описывается завершение кода класса  ``Main``  для отображения  ``FlowerFrame`` , подключения к веб-службе и вызова операций веб-службы.

1. Откройте класс  ``Main.java``  в редакторе.
image::images/main-empty.png[]
2. В теле класса инициализируйте переменную  ``int``  перед методом  ``main``  для числа загруженных изображений.

[source,java]
----

 private static int downloadedPictures;
----
3. Создайте в теле метода  ``main``  хранилище  ``HashMap``  для четырех изображений цветов и второе хранилище  ``HashMap``  для четырех эскизов.

[source,java]
----

final Map<String,Image> flowers = new HashMap<String,Image>(4);
final Map<String,Image> thumbs = new HashMap<String,Image>(4);
----
4. Добавьте операторы импорта  ``java.awt.Image`` ,  ``java.util.Map``  и  ``java.util.HashMap`` .
5. Добавьте в тело метода  ``main``  код для отображения  ``FlowerFrame`` .*// Show the FlowerFrame.*

[source,java]
----

final FlowerFrame frame = new FlowerFrame(flowers);
frame.setVisible(true);  
----
6. Добавьте в тело метода  ``main``  код для подключения клиента к веб-службе.*// The client connects to the service with this code.*

[source,java]
----

FlowerServiceService service = new FlowerServiceService();
final FlowerService port = service.getFlowerServicePort();
----
7. Добавьте операторы импорта для  ``org.flower.service.FlowerService``  и  ``org.flower.service.FlowerServiceService`` .
8. Добавьте в тело метода  ``main``  код для создания массива четырех потоков  ``Runnable``  и вызова операции  ``getFlower``  веб-службы в каждом потоке.*// The web service getFlower operation
// is called 4 times, each in a separate thread.
// When the operation finishes the picture is shown in
// a specific button.*

[source,java]
----

Runnable[] tasks = new Runnable[4];

for (int i=0; i<4;i++) {
    final int index = i;
    tasks[i] = new Runnable() {
        public void run() {
            try {
            
                *// Call the getFlower operation
                // on the web service:*
                Image img = port.getFlower(FlowerFrame.FLOWERS[index]);
                System.out.println("picture downloaded: "+FlowerFrame.FLOWERS[index]);
                     
                *// Add strings to the hashmap:*
                flowers.put(FlowerFrame.FLOWERS[index],img);
                        
                *// Call the showFlower operation
                // on the FlowerFrame:*
                frame.showFlower();
                        
            } catch (IOException_Exception ex) {
                ex.printStackTrace();
            }
            downloadedPictures++;
        }
    };
    new Thread(tasks[i]).start();
}
----
9. Добавьте оператор импорта для  ``org.flower.service.IOException_Exception`` .
10. Добавьте в тело метода  ``main``  код для вызова операции  ``getThumbnails``  веб-службы в отдельном потоке.*// The web service getThumbnails operation is called
// in a separate thread, just after the previous four threads finish.
// When the images are downloaded, the thumbnails are shown at 
// the bottom of the frame.*

[source,java]
----

Runnable thumbsTask = new Runnable() {
    public void run() {
        try {
            while (downloadedPictures < 4) {                        
                try {Thread.sleep(100);} catch (InterruptedException ex) {}
            }
                 
            *// Call the getThumbnails operation
            // on the web service:*
            List<Image> images = port.getThumbnails();
            System.out.println("thumbs downloaded");
                    
            if (images != null &amp;&amp; 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`` . Ниже представлен полный список операторов импорта:

[source,java]
----

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``  готов.


[source,java]
----

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);
        
        *// Show the FlowerFrame.*
        final FlowerFrame frame = new FlowerFrame(flowers);
        frame.setVisible(true);
        *        // The client connects to the service with this code.*
        FlowerService_Service service = new FlowerService_Service();
        final FlowerService port = service.getFlowerServicePort();
        
        Runnable[] tasks = new Runnable[4];
        
        *// The web service getFlower operation
        // is called 4 times, each in a separate thread.
        // When the operation finishes the picture is shown in
        // a specific button.*
        for (int i=0; i<4;i++) {
            final int index = i;
            tasks[i] = new Runnable() {
                public void run() {
                    try {
                    
                        *// Call the getFlower operation
                        // on the web service:*
                        Image img = port.getFlower(FlowerFrame.FLOWERS[index]);
                        System.out.println("picture downloaded: "+FlowerFrame.FLOWERS[index]);
                        
                        *// Add strings to the hashmap:*
                        flowers.put(FlowerFrame.FLOWERS[index],img);
                        
                        *// Call the showFlower operation
                        // on the FlowerFrame:*
                        frame.showFlower();
                        
                    } catch (IOException_Exception ex) {
                        ex.printStackTrace();
                    }
                    downloadedPictures++;
                }
            };
            new Thread(tasks[i]).start();
        }
        *// The web service getThumbnails operation is called
        // in a separate thread, just after the previous four threads finish.
        // When the images are downloaded, the thumbnails are shown at 
        // the bottom of the frame.*
        Runnable thumbsTask = new Runnable() {
            public void run() {
                try {
                    while (downloadedPictures < 4) {                        
                        try {Thread.sleep(100);} catch (InterruptedException ex) {}
                    }
                    
                    *// Call the getThumbnails operation
                    // on the web service:*
                    List<Image> images = port.getThumbnails();
                    System.out.println("thumbs downloaded");
                    
                    if (images != null &amp;&amp; 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 для вывода соответствующих изображений. Щелкните приложение правой кнопкой и выберите команду "Выполнить". Приложение Swing откроется, и через некоторое время диалоговое окно приложения заполнится изображениями, полученными из веб-службы. Если появляются не все изображения, необходимо очистить и построить проект FlowerService, а затем снова запустить его . Обратите внимание, что изображение в основном фрейме можно заменить, выбрав переключатель или нажав на эскиз.

]]

link:/about/contact_form.html?to=3&subject=Feedback:%20Flower%20Swing%20Client%20EE6[+Отправить отзыв по этому учебному курсу+]

Для отправки комментариев и предложений, получения поддержки и новостей о последних разработках, связанных с Java EE IDE NetBeans link:../../../community/lists/top.html[+присоединяйтесь к списку рассылки nbj2ee@netbeans.org+].

