Привет всем. Хочу поделиться решением одной задачи, которая встала передо мной некоторое время назад. Состояла она в том, что было необходимо дать веб-сервису возможность получать доступ и обмениваться информацией с некоторым устройством (в моем случае карт-ридер) через COM-порт на компьютере пользователя. Для этого было решено использовать Java-апплет, и в процессе реализации возникало несколько затруднений, решение которых я и хочу описать в статье. Замечу, что в интернете мне не удалось найти единого руководства «от начала и до конца», как решить мою задачу, так что надеюсь, кому-нибудь моя статья может сэкономить несколько часов времени.
Необходимо написать Java-апплет, загружаемый браузером с некоторого сайта, который сможет читать и записывать информацию в com-порт. При этом основным требованием является простота применения его для пользователя, отсутствие сложных настроек и по возможности необходимости инсталяции в систему пользователя каких-либо дополнительных библиотек или программ.
Требования к компьютеру пользователя:
1. Microsoft Windows XP, Vista, Seven
2. Любой современных браузер, поддерживающий Java-апплеты, основной упор на IE, Firefox
3. Установленная JRE1.6.
Как известно, в стандартную комплектацию JRE не входит какой-либо класс или метод, позволяющий осуществить доступ к COM-порту. Мною были найдены две наиболее популярные внешние библиотеки: javax.comm и RXTX. Посмотрев информацию о них, я принял решение о применении RXTX, т.к. javax.comm больше не развивается, к тому же у RXTX шире спектр поддерживаемых операционных систем, что может быть полезно мне в будущем.
Обе библиотеки осуществляют доступ к COM-порту через использование native-библиотек. Для RXTX — это библиотека rxtxSerial.dll.
В плане функций по работе с COM-портом обе библиотеки имеют идентичный набор классов.
Приведу код апплета, который мы будем «распространять». Этот апплет, естественно не тот, что я применял в своем проекте, его я сделал специально для данной статьи. Итак:
Постановка задачи
Необходимо написать Java-апплет, загружаемый браузером с некоторого сайта, который сможет читать и записывать информацию в com-порт. При этом основным требованием является простота применения его для пользователя, отсутствие сложных настроек и по возможности необходимости инсталяции в систему пользователя каких-либо дополнительных библиотек или программ.
Требования к компьютеру пользователя:
1. Microsoft Windows XP, Vista, Seven
2. Любой современных браузер, поддерживающий Java-апплеты, основной упор на IE, Firefox
3. Установленная JRE1.6.
Обзор имеющихся методов доступа к COM-порту из Java-апплетов
Как известно, в стандартную комплектацию JRE не входит какой-либо класс или метод, позволяющий осуществить доступ к COM-порту. Мною были найдены две наиболее популярные внешние библиотеки: javax.comm и RXTX. Посмотрев информацию о них, я принял решение о применении RXTX, т.к. javax.comm больше не развивается, к тому же у RXTX шире спектр поддерживаемых операционных систем, что может быть полезно мне в будущем.
Обе библиотеки осуществляют доступ к COM-порту через использование native-библиотек. Для RXTX — это библиотека rxtxSerial.dll.
В плане функций по работе с COM-портом обе библиотеки имеют идентичный набор классов.
Тестовый апплет для работы с COM-портом
Приведу код апплета, который мы будем «распространять». Этот апплет, естественно не тот, что я применял в своем проекте, его я сделал специально для данной статьи. Итак:
package cardreaders;
import java.applet.Applet;
import java.awt.Graphics;
import java.net.URL;
import java.io.InputStream;
import java.io.File;
import java.io.FileOutputStream;
import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
public class CardReader extends Applet {
Graphics gg;
int pos;
public void init() {
// TODO start asynchronous download of heavy resources
}
public void paint(Graphics g)
{
gg = g;
pos = 14;
text("INITIALIZATION...");
java.util.Enumeration<CommPortIdentifier> portEnum = CommPortIdentifier.getPortIdentifiers();
while ( portEnum.hasMoreElements() )
{
CommPortIdentifier portIdentifier = portEnum.nextElement();
if( portIdentifier.getPortType() == CommPortIdentifier.PORT_SERIAL)
text("FOUND PORT " + portIdentifier.getName());
}
}
public void text(String t)
{
gg.drawString(t, 10, pos);
pos += 14;
}
}
* This source code was highlighted with Source Code Highlighter.
Этот код просто выводит список всех найденных в системе COM-портов (при чем эта операция выполняется каждый раз при перерисовке апплета).
Добавление библиотеки RXTX
Исходя из постановки задачи, необходимо сделать наш апплет как можно более простым для пользователя. А нет ничего проще, чем один единственный jar-файл, включающий в себя и наш апплет и библиотеку RXTX. Для того чтобы интегрировать библиотеку в jar, прежде всего скачаем ее исходный код.
В скаченном архиве из папки src возьмем все .java файлы и добавим к нашему проекту. Если вы, как и я, работаете с NetBeans, то просто создайте в src-папке проекта структуру каталогов gnu/io, куда и поместите вышеуказанные файлы.
После компиляции в одном апплете сразу будут объеденены наш код и библиотека RXTX.
Добавление native-библиотеки rxtxSerial.dll
Как уже было отмечено выше, RXTX использует native-библиотеку rxtxSerial.dll для доступа к COM-порту под Windows. Эта библиотека подгружается классами RXTX через метод System.loadLibrary().
Поэтому для работы нашего апплета библиотека rxtxSerial.dll должна находиться на компьютере пользователя, причем в одной из директорый, откуда ее сможет загрузить RXTX, т.е. в директориях, прописаных в java.library.path. Одна из таких директорий — это директория bin в папке установки JRE.
Наиболее очевидным методом является предварительная инсталяция библиотеки rxtxSerial.dll в указанную директорию, но это требует дополнительных действий от пользователя (далеко не всегда квалифицированного), поэтому было применено другое решение.
Любой файл, в том числе и dll-библиотека может быть включен в jar-файл как ресурс. Чтобы сделать это в NetBeans просто создайте папку resources в директории, где находится .java файл вашего апплета, и поместите туда rxtxSerial.dll. Но как теперь подключить ее? На помощь приходит метод, который описан здесь.
С учетом описанного метода, исходный код нашего апплета становится таким:
package cardreaders;
import java.applet.Applet;
import java.awt.Graphics;
import java.net.URL;
import java.io.InputStream;
import java.io.File;
import java.io.FileOutputStream;
import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
public class CardReader extends Applet {
Graphics gg;
int pos;
static {
try {
System.out.println("CardReader {}");
/* Get DLL from JAR file */
URL res = CardReader.class.getResource("resources/rxtxSerial.dll");
InputStream is = res.openStream();
/* Define the destination file */
File dll = File.createTempFile("rxtxSerial",".dll");
/* Open the destination file */
FileOutputStream fos = new FileOutputStream(dll);
/* Copy the DLL fro the JAR to the filesystem */
byte[] array = new byte[1024];
for(int i=is.read(array);
i!=-1;
i=is.read(array)
) {
fos.write(array,0,i);
}
/* Close all streams */
fos.close();
is.close();
/* Load the DLL from the filesystem */
System.load(dll.getAbsolutePath());
System.out.println("CardReader loaded");
}
catch(Throwable e)
{
e.printStackTrace();
}
}
public void init() {
// TODO start asynchronous download of heavy resources
}
public void paint(Graphics g)
{
gg = g;
pos = 14;
text("INITIALIZATION...");
java.util.Enumeration<CommPortIdentifier> portEnum = CommPortIdentifier.getPortIdentifiers();
while ( portEnum.hasMoreElements() )
{
CommPortIdentifier portIdentifier = portEnum.nextElement();
if( portIdentifier.getPortType() == CommPortIdentifier.PORT_SERIAL)
text("FOUND PORT " + portIdentifier.getName());
}
}
public void text(String t)
{
gg.drawString(t, 10, pos);
pos += 14;
}
}
* This source code was highlighted with Source Code Highlighter.
Но это не все. Библиотека RXTX по-прежнему пытается загрузить rxtxSerial.dll с помощью System.loadLibrary(), что ей конечно не удается. Для решения этой проблемы можно было бы сохранять dll в одну из директорий java.library.path, но я пошел более простым путем: в исходных кодах RXTX закомментировал строчку загрузки библиотеки (она все равно грузится классом CardReader).
Подписывание апплета
Так как наш апплет пытается выполнить операции с файлами на компьютере пользователя, ему нужна цифровая подпись. Инструкций о том, как это сделать очень много в Интернете, например вы можете сделать, как написано здесь.
Итог
Распространяемый нами апплет получил возможность работы с COM-портом, и при этом все необходимое содержится в одном jar-файле. На пользовательском компьютере не требуется установки никаких библиотек, т.е. пользователю достаточно просто зайти на страницу с апплетом (и в нашем случае подтвердить в появившемся окне разрешение на запуск апплета).
P.S. Не хватает кармы для переноса статьи в соотвествующий блог.