Передача двоичных данных с помощью приложения веб-службы, часть 5: создание клиента Swing
Целью этого упражнения является создание клиента для веб-службы, которая была создана и развернута ранее, с последующим добавлением графического интерфейса пользователя в этот клиент. Интерфейс служит для вывода на экран изображений, переданных веб-службой в качестве двоичных данных.
В этом разделе рассматривается создание веб-приложения. В рамках приложения выполняется создание клиента, потребляющего веб-службу, которая была создана и изменена в предыдущих учебных курсах.
Создание приложения
Выберите "Файл" > "Новый проект" (Ctrl-Shift-N на Linux/Windows, ⌘-Shift-N на MacOS). Откроется мастер создания проекта.
Выберите "Приложение Java" в категории "Java". Нажмите кнопку "Далее". Появится мастер создания приложения Java. Введите текст FlowerClient в поле "Имя проекта". Выберите папку проекта и нажмите кнопку "Готово". В среде IDE будет создан новый проект приложения на Java.
Щелкните правой кнопкой мыши узел проекта FlowerClient и последовательно выберите в контекстном меню команды "Создать" > "Клиент веб-службы". Появится мастер создания клиента веб-службы.
Выберите переключатель "URL-адрес WSDL" и вставьте в соответствующее поле URL-адрес файла WSDL. По умолчанию URL-адресом является http://localhost:8080/FlowerAlbumService/FlowerServiceService?wsdl. URL-адрес можно найти в обозревателе, выполнив тестирование веб-службы и заменив текст ?Tester на ?wsdl в конце URL-адреса. Примите все прочие значения по умолчанию, включая пустое имя пакета.
Нажмите кнопку "Готово". В среде IDE выполняется загрузка файла WSDL, добавление заглушек клиента для взаимодействия с веб-службой, а также добавление узлов в проект приложения на Java в окне "Проекты".
Разработка формы JFrame
В этом разделе рассматривается добавление формы JFrame в веб-приложение и проектирование в рамках этого приложения графического интерфейса пользователя с использованием компонентов Swing. В заключение описывается привязка компонентов Swing к коду клиента веб-службы.
При отсутствии необходимости самостоятельного проектирования формы JFrame можно загрузить готовый файл JFrame Java здесь.
Щелкните правой кнопкой мыши узел FlowerClient и выберите пункт "Создать", а затем – "Форма JFrame". Присвойте фрейму имя FlowerFrame. Разместите его в пакете flowerclient.
Фрейм FlowerFrame будет открыт в редакторе. Откройте палитру (если она еще не открыта). Растяните нижнюю границу фрейма приблизительно на одну треть.
Перетащите панель JPanel из раздела "Контейнеры Swing" на палитре в фрейм FlowerFrame. Разверните панель, чтобы она заполнила все пространство фрейма FlowerFrame.
Щелкните правой кнопкой мыши панель в представлении "Проектирование". Выберите в контекстном меню пункт "Изменить имя переменной...". Присвойте панели имя gardenFlowersPanel.
Перетащите метку JLabel из палитры в верхнюю область панели gardenFlowersPanel. Щелкните метку правой кнопкой мыши и измените имя переменной метки на titleLabel. Снова щелкните правой кнопкой мыши метку titleLabel и выберите пункт "Изменить текст". Измените текст на "Garden Flowers". Можно также изучить свойства titleLabel и увеличить шрифт.
Перетащите группу кнопок в представление "Проектирование". Примите имя группы кнопок buttonGroup1, заданное по умолчанию.
Перетащите четыре переключателя в горизонтальный ряд под меткой titleLabel. В свойствах каждого переключателя укажите, что он является компонентом группы кнопок buttonGroup1. Для других свойств переключателей действительны следующие свойства:
Переключатели в группе кнопок buttonGroup1
Имя переменной
Выбрано
Текст
asterRadioButton
true
Aster
honeysuckleRadioButton
false
Honeysuckle
roseRadioButton
false
Rose
sunflowerRadioButton
false
Sunflower
Перетащите панель с прокруткой в область под переключателями. Разверните панель, чтобы она заполнила всю область по горизонтали и приблизительно две трети свободной области по вертикали. Измените имя переменной панели с прокруткой на mainScrollPane.
Перетащите панели на панель mainScrollPane. Измените имя переменной панели на mainPanel.
Щелкните правой кнопкой мыши панель mainPanel в представлении "Проектирование" и команду "Установить макет", а затем – "Макет с границами".
Перетащите кнопку на панель mainPanel. Поскольку панель mainPanel имеет границы, кнопка автоматически заполняет все пространство панели. Измените имя переменной кнопки на mainPictureButton, и введите для кнопки текст "Waiting for picture..."
Перетащите еще одну панель с прокруткой в область под панелью mainScrollPane. Разверните новую панель с прокруткой, чтобы она заполнила все свободное пространство. Измените имя переменной новой панели с прокруткой на thumbnailScrollPane.
Перетащите панель из палитры на панель thumbnailScrollPane. Измените имя переменной панели на thumbnailPanel. Установите в качестве формата панели thumbnailPanel формат сетки.
Перетащите четыре кнопки на панель thumbnailPanel. Поскольку панель thumbnailPanel имеет формат сетки, кнопки автоматически становятся одинакового размера и полностью заполняют панель. Свойства кнопок
Кнопки на панели thumbnailPanel
Имя переменной
Текст
asterButton
Ожидание...
honeysuckleButton
Ожидание...
roseButton
Ожидание
sunflowerButton
Ожидание...
Форма JFrame готова. На данный момент файл FlowerFrame выглядит следующим образом.
В этом разделе рассматривается инициализация компонентов в конструкторе и привязка компонентов к прослушивателям. Прослушиватели используются для вызова кода, который отвечает за отображение цветов.
В этом разделе описывается заполнение конструктора FlowerFrame
Перейдите в представление "Исходный код" в редакторе. Найдите начало тела класса FlowerFrame и конструктор FlowerFrame.
В верхней части тела класса фрейма FlowerFrame создайте над строкой конструктора массив строк с названиями всех цветов.
protected static final String[] FLOWERS = {"aster", "honeysuckle", "rose", "sunflower"};
Добавьте между массивом строк FLOWERS и конструктором строку, инициализирующую элемент java.util.Map с именемflowers. Карта ссылок принимает объект String и устанавливает его соответствие с объектом Image.
private Map<String, Image> flowers;
Добавьте операторы импорта для java.util.Map и java.awt.Image.
Добавьте код в конструктор FlowerFrame, чтобы связать определенный объект Image с определенным объектом String для определенного экземпляра карты ссылок flowers.
public FlowerFrame(Map<String, Image> flowers) {
this.flowers = flowers;
for (String flower:FLOWERS) {
flowers.put(flower,null);
}
initComponents();
}
Инициализируйте прослушиватели ItemListener для переключателей, а также прослушиватели 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);
}
Конструктор готов. В коде выводятся сообщения об ошибках, поскольку код не содержит классы RBListener и ButtonListener. Эти два класса представляют собой реализации прослушивателей ItemListener и ActionListener, соответственно. Запись этих классов в код рассматривается в следующем разделе.
В этом разделе рассматривается написание пользовательских прослушивателей для переключателей и кнопок цветов. Также рассматривается написание метода, который определяет цветок, выбираемый при нажатии кнопки, и получает объект Image цветка на карте ссылок flowers. В завершение описывается создание метода, который вызывается посредством класса Main и получает объект Image для каждого эскиза.
Найдите метод public static void main(String args[]) в теле класса FlowerFrame. Удалите этот метод и его документацию. (Вместо него в приложении будет использован класс Main.)
Напишите вместо метода main пользовательский прослушиватель ItemListener для переключателей. Этот прослушиватель служит для отображения нового изображения цветка при выборе переключателя.
private class RBListener implements ItemListener {
public void itemStateChanged(ItemEvent e) {
showFlower();
}
}
Запишите под пользовательским прослушивателем ItemListener пользовательский прослушиватель ActionListener для четырех кнопок цветов. При нажатии кнопки прослушиватель выбирает соответствующий переключатель:
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);
}
}
Напишите под пользовательским прослушивателем 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("");
}
Исправьте операторы импорта FlowerFrame, если они не были исправлены ранее при вставке в код. Для исправления всех операторов импорта одновременно щелкните их правой кнопкой мыши в диалоговом окне редактора и выберите в контекстном меню команду "Исправить операторы импорта". Ниже представлен полный список операторов импорта:
В этом разделе описывается завершение кода класса Main для отображения FlowerFrame, подключения к веб-службе и вызова операций веб-службы.
Откройте класс Main.java в редакторе.
В теле класса инициализируйте переменную int перед методом main для числа загруженных изображений.
private static int downloadedPictures;
Создайте в теле метода main хранилище HashMap для четырех изображений цветов и второе хранилище 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 код для создания массива четырех потоков Runnable и вызова операции getFlower веб-службы в каждом потоке.
// Операция 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 {
// Вызов операции getFlower
// в веб-службе:
Image img = port.getFlower(FlowerFrame.FLOWERS[index]);
System.out.println("picture downloaded: "+FlowerFrame.FLOWERS[index]);
// добавление строки в хранилище hashmap:
flowers.put(FlowerFrame.FLOWERS[index],img);
// Вызов операции showFlower
// для FlowerFrame:
frame.showFlower();
} catch (IOException_Exception ex) {
ex.printStackTrace();
}
downloadedPictures++;
}
};
new Thread(tasks[i]).start();
}
Добавьте оператор импорта для org.flower.service.IOException_Exception.
Добавьте в тело метода main код для вызова операции getThumbnails веб-службы в отдельном потоке.
// Операция веб-службы getThumbnails вызывается
// в отдельном потоке после четырех ранее созданных потоков.
// После загрузки изображений эскизы будут выведены
// в нижней части фрейма.
Runnable thumbsTask = new Runnable() {
public void run() {
try {
while (downloadedPictures < 4) {
try {Thread.sleep(100);} catch (InterruptedException ex) {}
}
// Вызов операции 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. Ниже представлен полный список выражений импорта:
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];
// Операция getFlower веб-службы
// вызывается 4 раза: по одному разу в каждом потоке.
// По завершении операции изображение выводится на
// определенной кнопке.
for (int i=0; i<4;i++) {
final int index = i;
tasks[i] = new Runnable() {
public void run() {
try {
// Вызов операции getFlower
// для веб-службы:
Image img = port.getFlower(FlowerFrame.FLOWERS[index]);
System.out.println("picture downloaded: "+FlowerFrame.FLOWERS[index]);
// Добавление строк в хранилище hashmap:
flowers.put(FlowerFrame.FLOWERS[index],img);
// Вызов операции showFlower
// в FlowerFrame:
frame.showFlower();
} catch (IOException_Exception ex) {
ex.printStackTrace();
}
downloadedPictures++;
}
};
new Thread(tasks[i]).start();
}
// Операция getThumbnails веб-службы вызывается
// в отдельном потоке сразу после четырех ранее созданных потоков.
// После загрузки изображений в нижней части фрейма
// будут выведены эскизы.
Runnable thumbsTask = new Runnable() {
public void run() {
try {
while (downloadedPictures < 4) {
try {Thread.sleep(100);} catch (InterruptedException ex) {}
}
// Вызов операции 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 для вывода соответствующих изображений. Щелкните приложение правой кнопкой и выберите команду "Выполнить". Приложение Swing откроется, и через некоторое время диалоговое окно приложения заполнится изображениями, полученными из веб-службы. Если появляются не все изображения, необходимо очистить и построить проект FlowerService, а затем снова запустить его . Обратите внимание, что изображение в основном фрейме можно заменить, выбрав переключатель или нажав на эскиз.
Для того, чтобы иметь возможность оставлять комментарии и предложения, обращаться за поддержкой и получать информацию о последних достижениях в области функциональных возможностей разработки для Java EE с помощью среды IDE NetBeans, подпишитесь на список рассылки .