Первое приложение (Avalanche — application framework for Java)
"Avalanche — application framework for Java" — реализация технологии стирающей различия между
вызовами локального и удаленного кода. Отказоустойчивость, масштабируемость,
модифицируемость, непрерывная доступность идут в комплекте приятными бонусами.
Все языки программирования предлагают для начала написать простую программу вывода
сообщения "Hello world". Этот пример не подходит для демонстрации функциональных
возможностей "Avalanche — application framework for Java", так как вызов этого примера всегда
возвращает одну и ту же строку и не позволяет идентифицировать источник, возвративший этот
результат.
Для первого приложения потребуется реализовать два класса (функцию и приложение), один
интерфейс (адаптер функции) и одну JSP страницу для отображения результата. Разработанное
приложение будет выполняться под управлением Tomcat.
Реализация класса функции — DemoFunction
В классе реализации функции нет ничего особенного, это обычный класс Java. В качестве
класса функции может быть использован любой класс. Пусть демонстрационный класс функции
возвращает следующую информацию из операционной системы: PID процесса и имя сервера;
версия операционной системы; название операционной системы.
Код класса DemoFunction.java
package ru.funsys.demo.avalanche; import java.lang.management.ManagementFactory; import java.util.Hashtable; public class DemoFunction { /** * Получить информацию из операционной системе.<br> * <br> * Из системных свойств выбираются значения<br> * <b>os.name</b> - имя ОС,<br> * <b>os.version</b> - версия ОС<br> * и <b>PID@name</b> - идентификатор процесса и имя сервера * * @return именованный список с параметрами ОС */ public Hashtable<String, String> getInfo() { Hashtable<String, String> result = new Hashtable<String, String>(); result.put("os.name", System.getProperty("os.name")); result.put("os.version", System.getProperty("os.version")); result.put("PID@name", ManagementFactory.getRuntimeMXBean().getName()); return result; } }
Реализация адаптера — DemoAdapter
С точки зрения языка программирования Java, адаптер — это интерфейс, в котором
декларируются методы класса функции, которые планируются вызывать с помощью этого
адаптера. Для класса DemoFunction требуется написать следующий код интерфейса
Код интерфейса DemoAdapter.java
package ru.funsys.demo.avalanche; import java.util.Hashtable; import ru.funsys.avalanche.AvalancheRemote; public interface DemoAdapter { public Hashtable<String, String> getInfo() throws AvalancheRemote; }
Следуют отметить, что все методы созданного интерфейса DemoAdapter должны
обязательно содержать декларирование исключения AvalancheException. Данная
реализация чем то напоминает реализацию RMI в языке программирования Java, но есть и
отличия:
- Класс функции (в приведенном примере — DemoFunction) не реализует интерфейс адаптера
(в приведенном примере — DemoAdapter) - В интерфейсе (пример — DemoAdapter) не обязательно нужно определять все публичные
методы класса функции (например, в DemoFunction), достаточно определить только методы,
которого будут вызываться через данный интерфейс.
Реализация адаптера позволяет вызывать методы функции, где бы она не была опубликована.
Реализация класса приложения — DemoApplication
Класс DemoApplication — выполняет вызов метода getInfo() класса DemoFunction
при помощи адаптера DemoAdapter. Пусть класс DemoApplication преобразует
полученный результат вызова getInfo() в формат TXT или HTML.
Код интерфейса DemoApplication.java
package ru.funsys.demo.avalanche; import java.util.Enumeration; import java.util.Hashtable; import ru.funsys.avalanche.Application; import ru.funsys.avalanche.AvalancheRemote; public class DemoApplication extends Application { /** * Определение поля для хранения экземпляра адаптера */ private DemoAdapter info; /** * Метод вызова метода адаптера и форматирования полученного результата * в текстовый формат или формат HTML в зависимости от параметра метода. * * @param html true, если необходим формат HTML, иначе false * * @return текс или таблицу HTML с параметрами ОС */ public String getInfo(boolean html) { StringBuilder builder = new StringBuilder(); if (html) { builder.append("<table border=\"1\">"); builder.append("<tr><th>key</th><th>value</th></tr>"); } try { // Вызов метода адаптера Hashtable<String, String> result = info.getInfo(); for (Enumeration<String> enumeration = result.keys(); enumeration.hasMoreElements(); ) { String key = enumeration.nextElement(); String value = result.get(key); if (html) { builder.append("<tr><td>").append(key).append("</td><td>").append(value).append("</td></tr>"); } else { builder.append(key).append(": ").append(value).append("\r\n"); } } } catch (AvalancheRemote e) { if (html) { builder.append("<tr><td>").append("error").append("</td><td>").append(e.getLocalizedMessage()).append("</td></tr>"); } else { builder.append("error").append(": ").append(e.getLocalizedMessage()).append("\r\n"); } } if (html) builder.append("</table>"); return builder.toString(); } }
К классу приложения предъявляются следующие требование: он обязательно должен наследоваться от класса ru.funsys.avalanche.Application.
Поле info определять не обязательно, ссылку на адаптер можно получить по имени, вызвав наследуемый метод getAdapter(String name) класса ru.funsys.avalanche.Application. Имя адаптера задается в конфигурационном файле приложения. Определение поля позволяет сократить объем кодирования.
Реализация JSP страницы — first.jsp
JSP страница отображает результат вызова метода функции в браузере. Сперва получается ссылка на класс DemoApplication и далее вызывается его метод getInfo c параметром true.
<%@ page import="ru.funsys.demo.avalanche.DemoApplication"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Демонстрационное приложение</title> </head> <body> <h1>Демонстрационное приложение</h1> <jsp:useBean id='DemoApp' scope='application' class='ru.funsys.demo.avalanche.DemoApplication'/> <%= DemoApp.getInfo(true) %> <br> </body> </html>
Реализация приложения завершена. Теперь нужно определить конфигурацию приложения.
Файл web.xml
В файле web.xml необходимо определить секцию запуска сервлета AvalancheServlet, указав в его параметрах имена конфигурационных файлов приложения и системы логгирования log4j. Сервлет AvalancheServlet инициирует все объекты приложения на основании файла конфигурации avalanche-config.xml.
<?xml version="1.0" encoding="UTF-8"?> <!-- web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0" --> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>Demo Avalanche</display-name> <description> Демонстрационное приложение с использованием framework Avalanche </description> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <servlet> <display-name>AvalancheServlet</display-name> <servlet-name>AvalancheServlet</servlet-name> <servlet-class>ru.funsys.servlet.http.AvalancheServlet</servlet-class> <init-param> <param-name>avalanche.config</param-name> <param-value>${catalina.base}/conf/avalanche-config.xml</param-value> </init-param> <init-param> <param-name>avalanche.log4j</param-name> <param-value>${catalina.base}/conf/avalanche-log4j.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> </web-app>
Файл конфигурации приложения avalanche-config.xml
Файл конфигурации приложения с локальным вызовом функции имеет следующий вид
<?xml version="1.0" encoding="UTF-8"?> <avalanche name="Demo Application"> <function class="ru.funsys.demo.avalanche.DemoFunction" name="info-function" description="Сведения об узле системы" /> <application class="ru.funsys.demo.avalanche.DemoApplication" name="DemoApp" > <adapter class="ru.funsys.demo.avalanche.DemoAdapter" name="info" uri="info-function" /> </application> </avalanche>
В этом файле определены две секции <function> и <application>. Секция <application> имеет вложенный элемент <adapter>, в котором в атрибуте uri указано имя локальной функции (см. значение атрибута name секции <function>).
Обращаю внимание на значение атрибута name секции <application>, значение этого атрибута DemoApp указывается в атрибуте id для получения ссылки на экземпляр класса DemoApplication в JSP странице.
Удаленный вызов функции DemoFunction
Вызов удаленной функции обычно требует дополнительной специализированной реализации серверной и клиентской части, предварительно выбрав какое то решения из множества существующих технологий, например REST, SOAP или какую то другую.
"Avalanche — application framework for Java" позволяет избежать дополнительного кодирования для вызова методов удаленной функции DemoFunction. Для этого достаточно изменить конфигурацию приложения и опубликовать копию приложения на удаленном сервере.
Необходимо добавить в конфигурационный файл avalanche-config.xml секции:
- <interface>, обеспечивает вызов метод удаленных экземпляров объектов по указанному протоколу
- <connector>, обеспечивает обращение к методам локальных функций с удаленных узлов приложения.
Удаленный вызов по протоколу RMI
Для обеспечения доступа к удаленным объектам по протоколу RMI в файл конфигурации необходимо добавить следующую секцию
<interface name="rmi-interface" uri="rmi://localhost:23000/rmi-connector" />
где:
- localhost определяет адрес удаленного узла
- 23000 определяет порт RMI Remote Server коннектора удаленного узла
- rmi-connector имя коннектора на удаленном узле
На удаленном узле требуется в конфигурационном файле добавить секцию <connector>
<connector class="RMIConnector" name="rmi-connector" port="23000" > <publish name="info" function="info-function" /> </connector>
где:
- rmi-connector имя коннектора
- 23000 порт RMI Remote Server
- <publish> публикует локальную функцию info-function в коннекторе, опубликованная функция info-function будет известна удаленным узлам под именем info
Удаленный вызов по протоколу HTTP
Для обеспечения доступа к удаленным объектам по протоколу HTTP в файл конфигурации необходимо добавить следующую секцию
<interface name="http-interface" uri="http://localhost:8080/demo/connector/http-connector" />
где:
- localhost определяет адрес удаленного узла
- 8080 определяет порт HTTP Server коннектора удаленного узла
- demo имя контекста удаленного экземпляра приложения
- connector имя сервлета AvalancheServlet
- http-connector имя коннектора на удаленном узле
На удаленном узле требуется в конфигурационном файле добавить секцию <connector>
<connector class="HTTPConnector" name="http-connector" > <publish name="info" function="info-function" /> </connector>
где:
- http-connector имя коннектора
- <publish> публикует локальную функцию info-function в коннекторе, опубликованная функция info-function будет известна удаленным узлам под именем info
Примечание! Значение HTTP порта не указывается, используется коннектор HTTP сервера (Tomcat).
Дополнительно нужно добавить секции <multipart-config> в конфигурацию сервлета AvalancheServlet и <servlet-mapping> для определения URI этого сервлета в файле web.xml.
Новая редакция файла web.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0" --> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>Demo Avalanche</display-name> <description> Демонстрационное приложение с использованием framework Avalanche </description> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <servlet> <display-name>AvalancheServlet</display-name> <servlet-name>AvalancheServlet</servlet-name> <servlet-class>ru.funsys.servlet.http.AvalancheServlet</servlet-class> <init-param> <param-name>avalanche.config</param-name> <param-value>${catalina.base}/conf/avalanche-config.xml</param-value> </init-param> <init-param> <param-name>avalanche.log4j</param-name> <param-value>${catalina.base}/conf/avalanche-log4j.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <multipart-config> <!-- 50MB max --> <max-file-size>52428800</max-file-size> <max-request-size>52428800</max-request-size> <file-size-threshold>0</file-size-threshold> </multipart-config> </servlet> <servlet-mapping> <servlet-name>AvalancheServlet</servlet-name> <url-pattern>/connector/*</url-pattern> </servlet-mapping> </web-app>
Чтобы сохранить работоспособность примера локального вызова функции, добавим две копии секции <application> с именами RMIApp и HTTPApp. Атрибуты uri вложенных элементов <adapter> этих секций примут составные значения имя-интерфейса/имя-удаленной функции, т.е. rmi-interface/info и http-interface/info.
Объединенная редакция конфигурационного файла avalanche-config.xml для локального и удаленного вызовов по протоколам RMI и HTTP
<?xml version="1.0" encoding="UTF-8"?> <avalanche name="Demo Application"> <interface name="rmi-interface" uri="rmi://localhost:23000/rmi-connector" /> <interface name="http-interface" uri="http://localhost:8080/demo/connector/http-connector" /> <function class="ru.funsys.demo.avalanche.DemoFunction" name="info-function" description="Сведения об узле системы" /> <application class="ru.funsys.demo.avalanche.DemoApplication" name="DemoApp" > <adapter class="ru.funsys.demo.avalanche.DemoAdapter" name="info" uri="info-function" /> </application> <application class="ru.funsys.demo.avalanche.DemoApplication" name="RMIApp" > <adapter class="ru.funsys.demo.avalanche.DemoAdapter" name="info" uri="rmi-interface/info" /> </application> <application class="ru.funsys.demo.avalanche.DemoApplication" name="HTTPApp" > <adapter class="ru.funsys.demo.avalanche.DemoAdapter" name="info" uri="http-interface/info" /> </application> <connector class="RMIConnector" name="rmi-connector" port="23000" > <publish name="info" function="info-function" /> </connector> <connector class="HTTPConnector" name="http-connector" > <publish name="info" function="info-function" /> </connector> </avalanche>
Для сохранения работоспособности JSP страницы first.jsp можно создать ее копии для вызова удаленной функции по протоколу RMI и HTTP, указав значения в атрибуте id RMIApp и HTTPApp соответственно.
Код rmi.jsp
<%@ page import="ru.funsys.demo.avalanche.DemoApplication"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Демонстрационное приложение - RMI</title> </head> <body> <h1>Демонстрационное приложение - RMI</h1> <p> Удаленный вызов - протокол RMI </p> <jsp:useBean id='RMIApp' scope='application' class='ru.funsys.demo.avalanche.DemoApplication'/> <%= RMIApp.getInfo(true) %> </body> </html>
Код http.jsp
<%@ page import="ru.funsys.demo.avalanche.DemoApplication"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Демонстрационное приложение - HTTP</title> </head> <body> <h1>Демонстрационное приложение - HTTP</h1> <p> Удаленный вызов - протокол HTTP </p> <jsp:useBean id='HTTPApp' scope='application' class='ru.funsys.demo.avalanche.DemoApplication'/> <%= HTTPApp.getInfo(true) %> </body> </html>
Теперь достаточно скопировать разработанное приложение на другой узел, при необходимости заменить значение localhost на адрес удаленного узла в секциях <interface> файла avalanche-config.xml, и проверить работоспособность приложения.
Как видно из приведенного примера, для обеспечения удаленного вызова функции не потребовалось вносить каких либо корректировок в программный код локального вызова.
Исходные коды, рассмотренного здесь примера приложения, опубликованы на GitHub
ссылка на оригинал статьи https://habr.com/ru/post/477350/
Добавить комментарий