410013796724260
• Webmoney
R335386147728
Z369087728698
Использование JNI в сервлетеДанная статья с примером использования динамической библиотеки .dll в сервлете является продолжением описания взаимодействия Java с C/C++ посредством JNI. Причины использования native методов в WEB-приложении могут быть разными, и их рассматривать не будем; будем считать, что определенную информацию необходимо обработать «нативной» библиотекой. В предыдущей статье была описана технология создания динамической библиотеки JNICall.dll с native методами. В нашем примере данную бибиотеку будем использовать в сервлете для выполнения функций умножения и конкатенации. Дополнительно в примере используем скриптовую библиотеку jQuery, которая позволит нам выполнить асинхронный ajax-запрос и разместить результат выполнения сервлетом функции на странице без ее перезагрузки. Пример использования библиотеки jQuery на странице JSP уже представлен на сайте, и Вы можете увидеть его здесь. Описание примера использования JNI в сервлетеСтруктура примера использования динамической библиотеки JNICall.dll в сервлете посредством JNI представлена на следующем скриншоте. Проект в IDE Eclipse включает :
Примечание : библиотека JNICall.dll создана для работы в Windows x64. Если Вам необходима библиотека для Windows x32, то можете создать новую библиотеку согласно технологии, описанной здесь. Листинг дескриптора приложения web.xmlВ дескрипторе приложения web.xml определен полный путь к сервлету (тег <servlet-class>) и алиас вызова сервлета из браузера (тег <url-pattern>). По умолчанию для сайта открывается страница index.jsp. <?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_2_5.xsd" version="2.5"> <display-name>Web Application</display-name> <servlet> <servlet-name>JNIServlet</servlet-name> <servlet-class> com.example.JNICallServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>JNIServlet</servlet-name> <url-pattern>/server</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> Листинг сервлета JNICallServletСервлет, при поступлении вызова, извлекает параметр "action", получает путь к контексту сервлета и загружает динамическую библиотеку при первичном обращении. После этого вызывает соответствующую параметру функцию в библиотеке, формирует ответ и возвращает результат в браузер. package com.example; import java.io.IOException; import java.io.OutputStream; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class JNICallServlet extends HttpServlet { private static final long serialVersionUID = 1L; private JNICall jniCall; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String action = request.getParameter("action").trim(); if(action == null || "".equals(action)) action = "undefined"; if (jniCall == null) { ServletContext context; context = request.getSession().getServletContext(); String path = context.getRealPath("/"); loadJNICall(path); } String answer = ""; if (jniCall != null) { try { if (action.equalsIgnoreCase("Multiply")) answer = "" + jniCall.doMultiply(12, 15); else if (action.equalsIgnoreCase("Concat")) answer = jniCall.doCombine("Hello, ", "world!"); else if (action.equalsIgnoreCase("Message")) answer = jniCall.getMessage("java message"); } catch (Exception e) { System.err.println (e.getMessage()); } } response.setContentType("text/plain"); OutputStream outStream = response.getOutputStream(); outStream.write(answer.getBytes("UTF-8")); outStream.flush(); outStream.close(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private void loadJNICall(String path) { if (jniCall != null) return; jniCall = new JNICall(path); } } Листинг класса загрузки DLL библиотекиМодуль загрузки динамической библиотеки JNICall в качестве параметра получает путь контекста и проверяет наличие библиотеки в поддиректории dll. Если библиотека найдена, то в соответствующее свойство загрузчика класса добавляется путь к директории JNICall.dll в методе addLibraryPath(String), и после этого загружается библиотека. Дополнительную информацию о настройке "java.library.path" можете получить здесь. package com.example; import java.io.File; import java.lang.reflect.Field; import java.util.Arrays; public class JNICall { public native int doMultiply(int val1, int val2); public native String doCombine (String str1, String str2); public native String getMessage(String message); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public JNICall(String path) { File file = new File(path + "/dll/JNICall.dll"); if (file.exists()) { path += "/dll"; try { addLibraryPath(path); System.loadLibrary("JNICall"); System.out.println("JNICall.dll is loaded"); } catch (Exception e) { e.printStackTrace(); } } else { System.err.println("JNICall.dll not found"); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public void addLibraryPath(String pathToAdd) throws Exception { Field usrPathsField; usrPathsField = ClassLoader.class.getDeclaredField("usr_paths"); usrPathsField.setAccessible(true); // get array of paths final String[] paths = (String[])usrPathsField.get(null); // check if the path to add is already present for (String path : paths) { if(path.equals(pathToAdd)) { return; } } // add the new path String[] newPaths = Arrays.copyOf(paths,paths.length+1); newPaths[newPaths.length-1] = pathToAdd; usrPathsField.set(null, newPaths); } } Листинг страницы index.jspВ секции head наряду с кодировкой и заголовком страницы определена скриптовая библиотека "jquery-3.2.1.min.js", загружаемая из директории "js". Можно, конечно, снять комментарий и загружать библиотеку из Интернета. Но не всегда это возможно, особенно если проект работает в закрытом Интранете. Поэтому скриптовая библиотека в минимальной комплектации (достаточно для решения задачи) подгружается из проекта. Кроме этого, в секции head определены скриптовые функции doAction, getChecked, callServlet, используемые для формирования ajax-запроса с параметром к сервлету и отображения результата выполнения функции сервлетом на странице. <%@ 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>JNI test</title> <!-- <script src="http://code.jquery.com/jquery-2.2.4.js" type="text/javascript"></script> --> <script src="js/jquery-3.2.1.min.js" type="text/javascript"></script> <script> function doAction(){ // Можно использовать jQuery // act = $('input[name=action]:checked').val(); act = getChecked(); callServlet(act); } function getChecked() { var inp = document.getElementsByName('action'); for (var i = 0; i < inp.length; i++) { if (inp[i].type == "radio" && inp[i].checked) { return inp[i].value; } } } function callServlet(el) { $.ajax({ url : 'server', data : { action : el }, success : function(response) { $('#answerId').text(response); } }); } </script> </head> <body> <p>Выберите функцию</p> <input type="radio" name="action" value="Multiply" CHECKED > Умножение 12 на 15 <p /> <input type="radio" name="action" value="Concat"> Конкатенация строк 'Hello, ' и 'world!'<p /> <input type="submit" value="Выполнить" onClick="doAction()"><p/> Ответ сервера : <span id="answerId" style="color:#46f; font-size:120%; font-weight:bold;"> </span> </body> </html> Интерфейс страницы включает две радиокнопки выбора функции и кнопку обращения к сервлету. При нажатии на кнопку «Выполнить» вызывается функция doAction(), которая определяет, какая из радиокнопок выбрана. Для этого можно использовать либо jQuery (закомментированные строки в методе doAction), либо скриптовую функцию getChecked(). После того, как выбранная функция определена, вызывается функция callServlet, которая формирует асинхронный ajax-запрос к сервлету с передачей ему параметра action (наименование функции). Результат выполнения сервлетом функции response поступит в функции success, которая разместит значение в поле answerId без перезагрузки страницы. Интерфейс страницы представлен на следующем скриншоте. Особенности использования JNI Скачать примерРассмотренный на странице пример, включающий проект Eclipse в технологии maven можно скачать здесь (633 Кб). |