вторник, 25 января 2011 г.

Поддержка нескольких мониторов и Java

Некоторое время назад была у меня задача развернуть форму на весь экран на дополнительном мониторе. Все достаточно просто, но как же я тогда ее решил. Я сделал следующим образом:
        // Определим форму на монитор
        setLocation(x, y);

        addWindowListener(new WindowAdapter() {

                @Override
                public void windowOpened(WindowEvent e) {
                    setExtendedState(JFrame.MAXIMIZED_BOTH);
                }
        });
Этот код можно поместить в конструктор формы и та распахнется на весь экран. Нюанс в том что тут x и y это координаты точки, которая попадает на требуемый монитор, на котором и требуется развернуть эту самую форму. Всё бы хорошо, но такая настройка не особо интуитивно понятная для большинства администраторов. Все же хочется как-то работать именно с мониторами а не с их пикселями.  И вот недавно попалась на глаза интересная информация. Можно работать с графическими устройствами получая все необходимые параметры. Используем для этого библиотеку AWT. В качестве демонстрации приведу код.

        import java.awt.GraphicsDevice;
        import java.awt.GraphicsEnvironment;
        ...
        GraphicsDevice[] screenDevices = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();
        for (GraphicsDevice graphicsDevice : screenDevices) {
            System.out.println("graphicsDevice = " + graphicsDevice.getIDstring() + " " + graphicsDevice.toString()
                    + "\nРазрешение экрана " + graphicsDevice.getDefaultConfiguration().getBounds().height + "x" + graphicsDevice.getDefaultConfiguration().getBounds().width
                    + "\nГлубина цвета " + graphicsDevice.getDisplayMode().getBitDepth()
                    + "\nЧастота " + graphicsDevice.getDisplayMode().getRefreshRate()
                    + "\nНачало координат " + graphicsDevice.getDefaultConfiguration().getBounds().x
                    + "-" + graphicsDevice.getDefaultConfiguration().getBounds().y);
        }


далее возможно развернуть форму на весь выбранный монитор таким способом

         graphicsDevice.setFullScreenWindow(form);

Есть конечно нюансы при использовании такого кода в Win и в Linux. К примеру в Win Vista с одним монитором будет

graphicsDevice = \Display0 D3DGraphicsDevice[screen=0]
Разрешение экрана 1080x1920
Глубина цвета 32
Частота 60
Начало координат 0-0
в Ubuntu10.04 с двумя мониторами будет так

graphicsDevice = :0.0 X11GraphicsDevice[screen=0]
Разрешение экрана 1080x1920
Глубина цвета-1
Частота 0
Начало координат 1680-0
graphicsDevice = :0.1 X11GraphicsDevice[screen=1]
Разрешение экрана 1050x1680
Глубина цвета -1
Частота 0
Начало координат 0-0
Что там случилось с параметрами я разбираться не стал. Ясно одно, определение мониторов, их разрешения и начала координат вполне нормально определяются. А значит это можно использовать для вывода формы на требуемый монитор и распахнуть форму на весь экран. При этом настройки будут вполне понятны как админам так и простым пользователям вашей программы.


воскресенье, 19 сентября 2010 г.

GearMan: распараллелевание и комуникация

Если вы сталкиваетесь с ресурсоемкой задачей, то приходится решать вопросы связанные с балансом нагрузки внутри системы, риском единой точки отказа , повышением скорости ответа системы и т.д. Для этого используются вспомогательные системы очередей сообщений. Прочитать про подобные системы можно в статье "Очередь сообщений - что это и зачем?". В нашем текущем проекте мы используем сервер Gearman для организации и распределения задач, по сути это сервер очереди сообщений. Кроме всего прочего сервер имеет реализации API для разных языков, это Perl, PHP, Python, Java, C#/.NET, а так же для JMS и баз данных PostgreSQL и MySQL.
В двух словах что же представляет собой работа с применением сервера Gearman.  Сервер является диспетчером сообщений к которому присоединяются клиентские части системы. Эти части делятся на два вида, назовем их воркеры и клиенты. Воркеры это части системы которые производят некоторую обработку данных, эту обработку и требуется распараллелить. Клиенты это части системы, которые требуют некой обработки данных и ради этого используют воркеры через сервер Gearman. Как это выглядит в проекте? Сложные ресурсоемкие процессы реализованы на Java и являются воркерами. Вэбклиент PHP использует эти процессы через сервер Gearman. Благодаря наличию API для разных языков программирования можно организовать эффективное взаимодействие внутри очень сложной системы с высоким уровнем изоляции частей друг от друга.
Что потребуется для подключение к серверу Gearman? Понадобится сам сервер и коннектор к нему для Java. Коннектор скачать можно вот отсюда. С ним все просто, достаточно подключить jar как библиотеку к своему проекту, при желании можно порыться в исходниках. При установки самого сервера могут потребоваться добавочные реепозитории. В этом вам могут помочь, особенно если вы используете Ubuntu, следующие ссылки: статья блога PHP+Gearman и некая документация.
С чего начать? Для начала не требуется какого-то массового курения мануалов. Достаточно заглянуть в пример, который идет вместе с коннектором в том же jar файле. Для этого изучите содержимое пакета  package org.gearman.example;
 
В качестве демонстрации простоты использования приведу несколько строк кода.
Worker: создать воркер из класса "WorkerClass" и зарегистрировать его

       List<Сlass<GearmanFunction>> functions = new ArrayList<Class<GearmanFunction>>();

       Class c = null;

       try {

           c = Class.forName(WorkerClass.class.getCanonicalName());

       } catch (ClassNotFoundException ex) {

       }

       functions.add((Class) c);

       new WorkerRunner(host, port, functions).start();





Client: создать клиента и вызвать необходимый воркер


         final GearmanClient client = new GearmanClientImpl();
        boolean addJobServer = client.addJobServer(new GearmanNIOJobServerConnection("192.168.0.0", Constants.GEARMAN_DEFAULT_TCP_PORT));
        byte[] data = ByteUtils.toUTF8Bytes("some data");
        GearmanJobResult res = null;
        final GearmanJob job = GearmanJobImpl.createJob(WorkerClass.class.getCanonicalName(), data, nill);
        client.submit(job);
        try {
            res = job.get();
        } catch (InterruptedException ex) {
            System.err.println(ex);
        } catch (ExecutionException ex) {
            System.err.println(ex);
        }
        String value = ByteUtils.fromUTF8Bytes(res.getResults());
        rc.shutdown();

пятница, 4 июня 2010 г.

JavaFX + Swing + Composer. Как встроить swing-контрол в форму JavaFX с использованием плагина Composer для NetBeans

JavaFX представляет собой достаточно новую и перспективную платформу создания визуальных интерфейсов с возможностью их использования как в web-приложения, так и в dektop. Палитра визуальных компонентов на данный момент в JavaFX весьма скудна и сами разработчики JavaFX рекомендуют использовать в качестве недостающих компоненты Swing. Встал вопрос: как добавить на форму JavaFX обычный компонент Swing. Для ускорения разработки визуальной части в NetBeans существует плагин Composer. Я использую этот плагин и отказываться от него мне не хотелось. Посмотрим что же в итоге получилось.

1. Как подготовить компонент Swing для размещения его на форме JavaFX.
//Создание нашего Swing контрола
var myB = new JButton();
//Сделаем наш контрол встраиваемым в приложение JavaFX с графической сценой
var myJavaFXB = SwingComponent.wrap(myB);
2. Как поместить наш подготовленный контрол в JavaFX сцену.
Класс сцены javafx.scene.Scene имеет поле content, который является списком всех контролов, лежащих в этой сцене. Наша задача поместить подготовленный нами Swing контрол в этот список.

3. Что делать если используем Composer.
Этот плагин помогает конструировать визуальную часть, при этом он генерирует часть кода, которая не доступна для редактирования из NetBeans. По моему, это очень замечательно, но требует изучения мысли разработчиков этого самого плагина. Найдем код, среди того что редактировать нельзя, который отвечает за класс нашей сцены с компонентом Swing. Выглядеть он может примерно так:
masterScene = javafx.scene.Scene {
            width: 640.0
            height: 480.0
            content: javafx.scene.layout.Panel {
                content: getDesignRootNodes ()
            }
};
Тут masterScene это моя визуальная сцена, куда требуется поместить контрол Swing. Прочитаем еще раз пункт 2 этой статьи. Мы имеем content, но не можем дописать эту часть кода прямо здесь, она не редактируемая.
Теперь откроем визуальный редактор Composer для редактирования нашей формы. В инспекторе объектов создаем метод, отрабатывающий по завершению создания нашей сцены. См. рис.
Теперь мы имеем метод, в который необходимо поместить логику добавления нашего подготовленного контрола в сцену. Примерно так.
 //нередактируемый код
masterScene = javafx.scene.Scene {
            width: 640.0
            height: 480.0
            content: javafx.scene.layout.Panel {
                content: getDesignRootNodes ()
            }
};
scenePost_CreationCode(); // <-- вызов нашего только что добавленного метода

... ... ...
// наш только что добавленный метод
function scenePost_CreationCode(): Void {
        // Предварительно установим некоторый значения и расположение на форме
        myB.setText("Нажми меня");
        myJavaFXB.height = 25;
        myJavaFXB.width = 75;
        myJavaFXB.layoutX = 10;
        myJavaFXB.layoutY =10;
        // Добавим наш подготовленный контрол в список сцены.
        insert myJavaFXB into getDesignScene().content;
}
Вот, собственно, и все.