Первое приложение (Avalanche — application framework for Java)

от автора

Первое приложение (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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *