410013796724260
• Webmoney
R335386147728
Z369087728698
Универсальный бандл templ-universalПример templ-universal, наследующий свойства базового модуля base-universal, можно использовать в качестве проектного шаблона при разработке интерфейсных модулей для приложений на платформе JaBricks. Структура проекта определена таким образом, чтобы иметь возможность создать интефейс либо в отдельном диалоговом окне, либо в виде панели. При этом, интерфейсную панель можно разместить как в контейнере главного фрейма, так и в контейнере (панели JPanel) другого бандла. Описание проектаНа скриншоте слева представлен проект templ-universal в IDE Eclipse. Структура проекта включает TemplActivator, играющего роль активатора бандла, двух классов (JDialogForm, JPanelForm), организующих создание интерфейсной формы, и класса CommonGUI, формирующего интерфейс. В зависимости от типа представления формы guimode активатор использует либо JDialogForm, либо JPanelForm, которые передают в CommonGUI свои контейнеры для создания интерфейса. В разделе проекта resources/properties размещаются ресурсные файлы, используемые для локализации интерфейса. Данный проект будет собран в виде четырех разноверсионных сборок для 14 урока. В зависимости от версии сборок бандлы будут по разному стартовать и формировать различный интерфейс. Так, сборки версии 1.0.0 и 2.0.0 будут стартованы главным фреймом с типом guimode='jpanel'. А бандлы 1.0.1 и 2.0.1 будут стартованы в качестве дочерних c guimode равным 'jpanel' и 'jdialog'. Бандл версии 1.0.0 интегрирует в свой контейнер интерфейс бандла 1.0.1, а бандл 2.0.0 откроет диалоговое окно бандла 2.0.1. Таким образом, в одном флаконе в виде проекта templ-universal, мы получаем родительские и дочерние бандлы разных версий. Конечно же, исходные коды отдельных дочерних и родительских бандлов были бы попроще, но очень малозначительно. К тому же, это упрощение сопряжено со значительных дублированием кода в разных бандлах. Поэтому, структурно, проект построен таким образом, чтобы все методы управления процессом создания интерфейса родительского и дочернего бандлов собрать в одном классе, в активаторе. При описании активатора, конечно же, методы родительского и дочернего бандлов будут прокомментированы и представлены наглядно, чтобы не было путаницы. Сначала рассмотрим два класса JPanelForm и JDialogForm, участвующих в организации процессов формирования интерфейса. После этого будет представлен класс создания интерфейса CommonGUI. На заключительном этапе будут подробно описаны методы активатора, формирующих механизмы «публикации/подписки» для получения типа представления интерфейса guimode и контейнеров главного фрейма и родительского бандла. В заключении будет представлен проектный файл pom.xml, отвечающий за процесс создания сборки/бандла. Класс JPanelFormКласс формирования интерфейсной панели JPanelForm наследует свойства родительского класса JBPanel. Конструктор класса в качестве параметров получает активатор и контейнер, после чего вызывает метод createPanel(Container) создания интерфейс панели. В методе createPanel(Container) сначала создается экземпляр интерфейсного класса gui и вызывается метод чтения сохраненных настроек интерфейса gui.readComponentParams(). После этого создается интерфейсная панель gui.createDesk(), которая размещается в контейнере фрейма container.add(gui.getPnlDesk()). На следующем шаге создается интерфейс : gui.createGUI(gui.getPnlDesk()). Создаваемая классом CommonGUI панель бандла 1.0.0 частично будет использована для размещения интерфейса дочернего бандла версии 1.0.1. Методом getContainer4Bundle() будем получать этот контейнер для отправки дочернему бандлу. А предварительно используем его для размещения в нижней части метки с временем старта приложения и временем сохранения регулируемых настроек интерфейса панели. Данную функцию выполнит метод createStartTimeLabel. Метод createStartTimeLabel получает от активатора время старта приложения (activator.getStartTime) и время сохранения регулируемых настроек (getSaveDateTime). Разность времени позволяет определить актуальность сохраненных параметров интерфейса.
import org.jabricks.universal.JBPanel; import org.jabricks.universal.ComponentParams; import org.jabricks.widgets.constants.IWidgetsConstants; @SuppressWarnings("serial") public class JPanelForm extends JBPanel implements IWidgetsConstants { private TemplActivator activator = null; private CommonGUI gui = null; //----------------------------------------------------- public JPanelForm(TemplActivator activator, Container container) { super(activator.getBundleContext()); this.activator = activator; createPanel(container); } //----------------------------------------------------- @Override protected void createPanel(Container container) { // Создание интерфейсного класса gui = new CommonGUI(context, this, GUIMode_jpanel); // Чтение сохраненных настроек gui.readComponentParams(); gui.setActivator(activator); // Создание интефейсной панели gui.createDesk(); // Размещение интефейсной панели в контейнере container.add(gui.getPnlDesk()); gui.getPnlDesk().setVisible(false); // Создание интерфейса gui.createGUI(gui.getPnlDesk()); // Контейнер размещения интерфейса дочернего бандла JPanel pnl = (JPanel) gui.getContainer4Bundle(); if (pnl != null) createStartTimeLabel(pnl); container.doLayout(); gui.getPnlDesk().doLayout(); gui.getPnlDesk().setVisible(true); } //----------------------------------------------------- private void createStartTimeLabel(JPanel pnl) { SimpleDateFormat sdf; sdf = new SimpleDateFormat("dd.MM.yyyy hh:mm:ss"); JLabel lbl = new JLabel(); String active = "ACTIVE"; Date dt = new Date(activator.getStartTime()); String start_time = sdf.format(dt); // Время сохранения String save_time = "null"; if (getSaveDateTime() != null) { long offs = activator.getStartTime() - getSaveDateTime().getTime(); if (offs > 0) active = "NOT ACTIVE"; save_time = sdf.format(getSaveDateTime()); } lbl.setText(start_time + " : " + active + " ( " + save_time + " )"); lbl.setForeground(Color.ORANGE); pnl.setLayout(new BorderLayout()); pnl.add(lbl, BorderLayout.SOUTH); pnl.doLayout(); } //----------------------------------------------------- public Container getContainer4Bundle() { return gui.getContainer4Bundle(); } //----------------------------------------------------- @Override public void saveComponentParams() { if (gui != null) ((CommonGUI)gui).saveComponentParams(); } //----------------------------------------------------- @Override public JPanel getJPanel() { return ((CommonGUI)gui).getPnlDesk(); } //----------------------------------------------------- } На следующем скриншоте представлен интерфейс бандла версии 1.0.0 после старта. Панель бандла размещена в контейнере главного фрейма. Версия бандла отображена в верхней части правой панели. В нижней части левой панели представлены время старта приложения, время последнего сохранения настроек бандла, которые не являются актуальными. Класс JDialogFormКласс формирования диалогового окна JDialogForm наследует свойства родительского класса JBDialog. Создание интерфейса выполняется в конструкторе. Сначала определяется иконка окна, после чего выполняется чтение регулируемых настроек интерфейса и определение размеров окна. Для формирования интерфейса окна создается экземпляр класса CommonGUI, которому передаются все необходимые объекты. Метод getContainer4Bundle также, как и в объекте панели, используется в CommonGUI для отправки активатором дочернему бандлу контейнера размещения интерфейса.
import java.awt.Container; import java.awt.Dimension; import java.awt.BorderLayout; import org.jabricks.resources.IResources; import org.jabricks.resources.ResourcesImpl; import org.jabricks.universal.ComponentParams; import org.jabricks.widgets.constants.IWidgetsConstants; import org.jabricks.universal.JBDialog; public class JDialogForm extends JBDialog implements IWidgetsConstants { private int DEFAULT_WIDTH = 600; private int DEFAULT_HEIGHT = 400; private CommonGUI gui = null; //----------------------------------------------------- public JDialogForm(TemplActivator activator) { super(activator.getBundleContext()); // Определение иконки IResources rsc = new ResourcesImpl(); if (rsc != null) getJDialog().setIconImage(rsc.getAppIcon()); // Создание интерфейса getJDialog().setVisible(false); gui = new CommonGUI(context, this, GUIMode_jdialog); gui.readComponentParams(); gui.setActivator(activator); gui.createDesk(); // Получение панели содержимого Container container = getJDialog().getContentPane(); // Размещение панели desk container.add(gui.getPnlDesk(),BorderLayout.CENTER); gui.createGUI(gui.getPnlDesk()); // Расположение поверх остальных getJDialog().setAlwaysOnTop(true); if (gui.form_dim != null) { getJDialog().setSize(gui.form_dim); DEFAULT_WIDTH = gui.form_dim.width; DEFAULT_HEIGHT = gui.form_dim.height; } Dimension dim; dim = Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); getJDialog().setPreferredSize(dim); // Центрирование формы formCentering(DEFAULT_WIDTH, DEFAULT_HEIGHT); getJDialog().setVisible(true); } //----------------------------------------------------- @Override public void saveComponentParams() { if (gui != null) ((CommonGUI)gui).saveComponentParams(); } //----------------------------------------------------- public Container getContainer4Bundle() { return gui.getContainer4Bundle(); } } На следующем скриншоте на переднем плане представлен интерфейс бандла версии 2.0.1 в виде диалогового окна; версия бандла отображена в титульной строке. На заднем фоне представлен интерфейс родительского бандла версии 2.0.0, размещенный в контейнере главного фрейма. В нижней части левой панели родительского бандла представлены время старта приложения, время последнего сохранения настроек бандла, которые являются актуальными. Класс CommonGUIКласс CommonGUI формирует интерфейс бандла. В зависимости от версии в интерфейсе размещаются кнопки для старта дочернего бандла или закрытия окна. В таблице представлены методы класса CommonGUI с описанием их назначения. Ниже таблицы представлен листинг класса.
Активатор TemplActivatorРассмотрим исходный код активатора по частям, чтобы понять назначение и взаимосвязь между собой отдельных методов. Методы активатора условно можно объединить по следующим решаемым задачам :
Полный текст проекта с исходными кодами Вы можете скачать в конце страницы. Поля активатораАктиватор наследует свойства UniversalActivator базового модуля base-universal. В переменных активатора определен только массив взаимосвязи используемых в приложении бандлов bundles. Все бандлы имеют одинаковые SymbolicName, включающий groupId и artifactId. Отличия касаются только версий. Поэтому в массиве bundles приведены только версии бандлов. Так бандл версии 1.0.0 будет стартовать бандл 1.0.1 в режиме guimode='jpanel', а бандл 2.0.0 стартует 2.0.1 в guimode='jdialog'. Методы активатора представлены ниже. public class TemplActivator extends UniversalActivator implements IWidgetsConstants { private String[][] bundles={ {"1.0.0","jpanel" ,"1.0.1"}, {"2.0.0","jdialog","2.0.1"}}; //-------------------------------------------------- // Методы активатора ... } Старт бандлаСтарт бандла вызовом метода активатора start выполняет фреймворк. При старте бандл создает двух подписчиков и отправляет сообщение с запросом времени старта приложения. При получении ответов подписчики вызовут методы активатора changeLocale и getStartTimeResponseMessage. Метод активатора getStartTime() используется классом JPanelForm для определения актуальности сохраненных настроек. После подписчиков бандл переходит к определению типа представления интерфейса guimode вызовом метода getGuiMode
@Override public void start(final BundleContext context) throws Exception { super.start(context); // Подключение слушателей ("подписка") createStartTimeResponseSubscriber(this); createLocaleSubscriber (); // Отправка запроса времени старта приложения sendStartTimeRequestEvent(); // Определение режима представления getGuiMode(); } //--------------------------------------------------------- @Override public void changeLocale (Locale locale) { if (jdialog != null) ((JDialogForm)jdialog).changeLocale(locale); else if (jpanel != null) ((JPanelForm)jpanel).changeLocale(locale); } //--------------------------------------------------------- @Override public void getStartTimeResponseMessage(long start_time) { super.getStartTimeResponseMessage(start_time); } Примечание : Старт дочернего бандлаСамо название подзаголовка говорит уже о том, что представленные ниже методы относятся к родительскому бандлу. Чтобы стартовать дочерний бандл необходимо :
Таким образом, представленные в листинге методы будут использованы в родительских бандлах версий 1.0.0 и 2.0.0.
private Bundle getChildBundle() { Bundle child = null; String child_v = null; // GAV параметры бандла Bundle bnd = context.getBundle(); String sn = bnd.getSymbolicName(); String ver = bnd.getVersion().toString(); // Версия дочернего бандла for (int i = 0; i < bundles.length; i++) { if (bundles[i][0].equals(ver)) { child_v = bundles[i][2]; break; } } // Определение дочернего бандла for (int i=0; i < context.getBundles().length; i++) { bnd = context.getBundles()[i]; String bnd_sn; String bnd_ver; bnd_sn = bnd.getSymbolicName(); bnd_ver = bnd.getVersion().toString(); if (bnd_sn.equalsIgnoreCase(sn) && bnd_ver.equals(child_v)) { child = bnd; break; } } return child; } //--------------------------------------------------------- public void startChildBundle() { Bundle child = getChildBundle(); if ((child != null) && (child.getState() != Bundle.ACTIVE)) { createGuimodeRequestSubscriber(); try { child.start(); } catch (BundleException e) {} } } Определение типа интерфейсаВызванный при старте бандла родительский метод getGuiMode() определяет тип представления интерфейса guimode. Если бандл стартован из главного фрейма, то guimode определен в структурах описания строки «Главного меню» или «Панели инструментов». При guimode='jdialog' будет вызван метод активатора createDialogGUI(), при guimode='jpanel' автоматически будет стартован соответствующий подписчик и запрошен контейнер главного фрейма; подписчик вызовет метод активатора getJFrameResponseContainer, которому передаст контейнер главного фрейма. Таким образом, если бандл стартован главным фреймом, то в зависимости от guimode вызывается один из методов активатора (createDialogGUI, getJFrameResponseContainer), которые необходимо переопределить в наследнике. Если бандл является дочерним и стартован из другого (родительского) бандла, то родитель должен стартовать подписчика запроса режима представления интерфейса дочернего бандла. Подписчик стартуется методом createGuimodeRequestSubscriber (см. в описании старта дочернего бандла). Стартовать подписчика необходимо только в том случае, если дочерний бандл будет запрашивать режим представления интерфейса guimode, т.е. если он создает интерфейс с использованием базового модуля base-universal. При поступлении запроса подписчик вызовет метод активатора getGuimodeRequestMessage, который должен отправить дочернему бандлу ответ. Дочерний бандл, прежде чем отправлять запрос режима представления интерфейса должен стартовать подписчика. Родительский метод отправки запроса sendGuimodeRequestEvent сам стартует подписчика вызовом createGuimodeResponseSubcriber. При поступлении ответа подписчик вызовет метод активатора getGuimodeResponseMessage(String), которому передаст режим представления интерфейса. В зависимости от значения guimode сразу же будет создано диалоговое окно (createDialogGUI), либо активатор перейдет к запросу контейнера родительского бандла.
/** * Создание диалогового окна */ @Override protected void createDialogGUI() { jdialog = new JDialogForm(context); ((JDialogForm)jdialog).addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent we) { jdialog.setVisible(false); try { // Останов бандла context.getBundle().stop(); } catch (Exception e) {} } }); } //--------------------------------------------------------- /** * Поступление контейнера главного фрейма */ @Override public void getJFrameResponseContainer (Container container) { jpanel = new JPanelForm(this, container); } //--------------------------------------------------------- private String getChildGuimode() { Bundle bnd = context.getBundle(); String gui_mode = null; String version = bnd.getVersion().toString(); for (int i = 0; i < bundles.length; i++){ if (bundles[i][0].equals(version)) { gui_mode = bundles[i][1]; break; } } return gui_mode; } //--------------------------------------------------------- /** * Метод обработки запроса режима представления GUI * родительским бандлом */ @Override public void getGuimodeRequestMessage() { super.getGuimodeRequestMessage(); String guimode = getChildGuimode(); if (guimode.equalsIgnoreCase(GUIMode_jpanel)) // Подписчик для запроса панели createPanelRequestSubscriber(); // Отправка сообщения с guimode sendGuimodeResponseEvent (guimode); } //--------------------------------------------------------- /** * Метод обработки ответа режима представления GUI * дочерним бандлом */ @Override public void getGuimodeResponseMessage(String guimode) { super.getGuimodeResponseMessage(null); if (guimode.equalsIgnoreCase(GUIMode_jdialog)) createDialogGUI(); else // send request sendPanelRequestEvent(); } Запрос контейнера родительского бандлаДочерний бандл, получив по подписке тип представления интерфейса guimode= 'jpanel', выполняет запрос контейнера родительского бандла методом sendPanelRequestEvent (см. листинг выше), который сразу же стартует подписчика на ответ. Подписчик родительского бандла на данный запрос при получении сообщения вызывает переопределенный метод активатора getPanelRequestMessage, который закрывает регистрацию подписки на данный тип сообщений, получает контейнер методом getContainer, очищает контейнер и отправляет его дочернему бандлу методом sendPanelResponseEvent. Подписчик дочернего бандла на контейнер родителя вызывает метод активатора getPanelResponseContainer, в котором бандл закрывает подписку на данный тип сообщений и создает интерфейс в виде панели.
private Container getContainer() { Container c = null; if (jdialog != null) c = ((JDialogForm)jdialog).getContainer4Bundle(); else if (jpanel != null) c = ((JPanelForm)jpanel).getContainer4Bundle(); return c; } //--------------------------------------------------------- @Override public void getPanelRequestMessage() { super.getPanelRequestMessage(); Container container = getContainer(); if (container != null) sendPanelResponseEvent(container); } //--------------------------------------------------------- private void clearContainer (JPanel panel) { for (int i = panel.getComponentCount()-1; i >= 0; i--) panel.remove(i); } //--------------------------------------------------------- @Override public void getPanelResponseContainer(Container container) { super.getPanelResponseContainer(null); if (container != null) { clearContainer ((JPanel) container); jpanel = new JPanelForm(this, container); } } Останов бандлаПри останове бандла вызывается метод stop (BundleContext), который должен остановить стартованный дочерний бандл. Вызываемый при останове переопределенный метод saveComponentParams вызывает родительский метод сохранения настроек интерфейса.
@Override public void stop(BundleContext context) throws Exception { Bundle child = getChildBundle(); if ((child != null) && (child.getState() == Bundle.ACTIVE)) child.stop(); super.stop(context); } //--------------------------------------------------------- @Override public void saveComponentParams() { if (jdialog != null) ((JDialogForm)jdialog).saveComponentParams(); else if (jpanel != null) ((JPanelForm)jpanel).saveComponentParams(); } На следующем скриншоте представлен интерфейс бандлов версий 1.0.0 и 1.0.1. В контейнере главного фрейма размещен компонент JSplitPane бандла 1.0.0. Сначала был стартован бандл 1.0.0, после чего кнопкой «Стартовать бандл» был запущен бандл 1.0.1, который разместил в левой части компонента JSplitPane родительского бандла свою панель. Версии бандлов отображены в верхних частях компонента JSplitPane. Проектный файл pom.xmlПроектный файл включает секции описания GAV-параметров, свойств, зависимостей и сборки. Вид сборки в теге <packaging> определен как bundle. В секции сборки используется плагин maven-bundle-plugin, в теге которого <Bundle-Activator> необходимо указать класс активатора. Данный бандл используется в Уроке 14, для которого неообходимо создать 4 одинаковые сборки разных версий (1.0.0, 1.0.1, 2.0.0, 2.0.1). Для этого следует выполнить сборку бандла при разных значениях тега <version>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.jabricks.templates</groupId> <artifactId>templ-universal</artifactId> <packaging>bundle</packaging> <version>1.0.0</version> <name>JPanel template</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding> UTF-8 </project.build.sourceEncoding> <maven.test.skip>true</maven.test.skip> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.apache.felix</groupId> <artifactId> org.apache.felix.framework </artifactId> <version>5.6.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.felix</groupId> <artifactId> org.apache.felix.eventadmin </artifactId> <version>1.2.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.jabricks.resources</groupId> <artifactId>util-resources</artifactId> <version>1.0.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.jabricks.widgets</groupId> <artifactId>gui-widgets</artifactId> <version>1.0.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.jabricks.universal</groupId> <artifactId>base-universal</artifactId> <version>1.0.1</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> </configuration> </plugin> <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <version>2.3.7</version> <extensions>true</extensions> <configuration> <instructions> <Bundle-SymbolicName> ${project.groupId}.${project.artifactId} </Bundle-SymbolicName> <Bundle-Name> ${project.name} </Bundle-Name> <Bundle-Version> ${project.version} </Bundle-Version> <Bundle-Activator> ${project.groupId}.TemplActivator </Bundle-Activator> <Import-Package> javax.swing.*, org.osgi.framework.*, org.jabricks.widgets.*, org.jabricks.universal.*, org.jabricks.resources </Import-Package> </instructions> </configuration> </plugin> </plugins> </build> </project> ЗависимостиМодуль templ-universal функционирует вместе со следующими бандлами приложения JaBricks :
Кроме данных бандлов платформы JaBricks модуль templ-universal имеет следующие зависимости, загружаемые фреймворком при старте приложения :
Подключение модуля templ-universalДля подключения модуля templ-universal в приложение JaBricks необходимо :
GAV параметры модуля templ-universal имеют следующие значения :
Подробнее о включении модуля в приложение Jabricks представлено здесь. Скачать templ-universalРассмотренный на странице пример templ-universal для создания интерфейса с использованием базового модуля base-universal в виде проекта IDE Eclipse упакован в архивный файл templ-universal.zip (42.5 Кб). |