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 Кб). |
