可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm trying to load the smartcard terminals using the javax.smartcardio API with the following code:
public CardTerminal getReadyCardTerminal() throws CardException {
TerminalFactory factory = TerminalFactory.getDefault();
CardTerminals terminals = factory.terminals();
List<CardTerminal> list = terminals.list(State.CARD_PRESENT);
while (list.isEmpty()) {
terminals.waitForChange(1000);
list = terminals.list(State.CARD_PRESENT);
}
CardTerminal cardTerminal = list.get(0);
return cardTerminal;
}
... and I always get the following exception:
java.lang.IllegalStateException: no terminals
at javax.smartcardio.TerminalFactory$NoneCardTerminals.waitForChange(TerminalFactory.java:145)
On Windows Vista/7 everything works fine, but I can't get it to work on Linux. I'm using Ubuntu 12.04 64 bits.
I installed the pcscd service using the following command:
sudo apt-get install libccid pcscd libpcsclite-dev libpcsclite1
sudo service pcscd start
And the pcsc_scan command prints this:
PC/SC device scanner
V 1.4.18 (c) 2001-2011, Ludovic Rousseau <ludovic.rousseau@free.fr>
Compiled with PC/SC lite version: 1.7.4
Using reader plug'n play mechanism
Scanning present readers...
0: OMNIKEY CardMan 3x21 00 00
Tue Sep 11 15:44:49 2012
Reader 0: OMNIKEY CardMan 3x21 00 00
Card state: Card inserted,
ATR: <some hexa codes>
...
So everything looks ok, but the smartcardio just doesn't work. I'm trying with both Oracle and OpenJDK 1.7.0_05, 32 and 64 bits.
The code runs ok with OpenJDK (but not with Oracle JDK, don't know really why) on a Ubuntu 32 bits environment. So I think it is a problem with the 64 bits bridge from Java to the PC/SC library.
Any ideas?
Thanks.
回答1:
I think I found a workaround for this as I just had a similar problem. In a bugreport from ubuntu it says that the javax.smartcardio library searches for the PC/SC library in the wrong directory.
By specifying the path to the PC/SC library on my machine, like the bugreport mentions, I got it working.
The paths in the bugreport are wrong for me, I'm on 64 bit fedora, where the pc/sc library are installed at /usr/lib64/libpcsclite.so.1
So the workaround for me is to specify the library path to java like this:
java -Dsun.security.smartcardio.library=/usr/lib64/libpcsclite.so.1
Depending on your Linux distribution, the location of libpcsclite.so.1
actually might differ, it could also be at /lib/x86_64-linux-gnu/libpcsclite.so.1
(i.e. Kubuntu 15.04).
In that case, call it like this:
java -Dsun.security.smartcardio.library=/lib/x86_64-linux-gnu/libpcsclite.so.1
回答2:
i'm using raspberry with debian arm version
find the location of libpcsclite first with:
$ ldd -r /usr/bin/pcsc_scan
and then use the libpcsclite location with:
java -Dsun.security.smartcardio.library=/usr/lib/arm-linux-gnueabihf/libpcsclite.so.1
回答3:
For anyone else struggling with this on Ubuntu 14 with a 64 bit machine. I found the .so file is actually located in the following directory
/usr/lib/x86_64-linux-gnu/libpcsclite.so
So running my app with the setting as below worked for me
-Dsun.security.smartcardio.library=/usr/lib/x86_64-linux-gnu/libpcsclite.so
回答4:
You need to give the path to the libpcsclite.so.1 when calling your program as follows
java -Dsun.security.smartcardio.library=/path/to/libpcsclite.so.1
If you don't know the path to the library, use the following command
find /usr/lib -name libpcsclite.so.1
This usually shows you the path on your machine. I used it on both Ubuntu 10 (32bit) and Ubuntu 15(32bit and 64bit)
If you're lazy like me, what you can do is include this part of code in your program before you use the javax.smartcardio library
try {
String comm[] = { "find", "/usr", "/lib", "-name",
"libpcsclite.so.1" };
Process p = Runtime.getRuntime().exec(comm);
BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream()));
while ((line = reader.readLine()) != null && !line.equals("")) {
if (line.contains("libpcsclite.so.1")) {
System.setProperty("sun.security.smartcardio.library",line);
break;
}
}
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
Now you can run your code from as usual without including the path to libpcsclite.so.1
回答5:
Addition to the solution with supplying the path as a parameter like this:
java -Dsun.security.smartcardio.library=/usr/lib64/libpcsclite.so.1
If you don't want to supply this every time you call the JVM, set it in the environment variables _JAVA_OPTIONS and/or JAVA_OPTS:
export _JAVA_OPTIONS="-Dsun.security.smartcardio.library=/usr/lib64/libpcsclite.so.1"
export JAVA_OPTS="-Dsun.security.smartcardio.library=/usr/lib64/libpcsclite.so.1"
Since this is a workaround for bug that affects the entire system, it makes sense IMHO to apply this workaround systemwide as well.
JAVA_OPTS has local scope and has to be evaluated by scripts running your code; _JAVA_OPTIONS is supposed to be evaluated automatically by the JRE.
回答6:
Yet another approach (my favorite) is to make some symbolic links.
It has the advantage that it works system-wide (no jvm arguments, no environment variables).
For my (beloved) debian jessie amd64:
ln -s /usr/lib/x86_64-linux-gnu/libpcsclite.so libpcsclite.so
ln -s /usr/lib/x86_64-linux-gnu/libpcsclite.so.1 libpcsclite.so.1
ln -s /usr/lib/x86_64-linux-gnu/libpcsclite.so.1.0.0 libpcsclite.so.1.0.0
Note: This will probably require superuser access.
回答7:
Complementing @AshanPerera answer, as sometimes searching each time can be slow, you can search it at the first time, and them store the location in a file, and read it from then on:
try {
String filename = "libpcsclite.location";
File propertyFile = new File(filename);
if(propertyFile.createNewFile())
{
String commandWithArguments[] = { "find", "/usr", "/lib", "-name","libpcsclite.so.1" };
Process searchProcess = Runtime.getRuntime().exec(commandWithArguments);
BufferedReader searchReader = new BufferedReader(new InputStreamReader(searchProcess.getInputStream()));
String propertyValue;
while ( (propertyValue = searchReader.readLine()) != null && !propertyValue.equals(""))
{
if (propertyValue.contains("libpcsclite.so.1")) {
BufferedWriter propertyWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(propertyFile)));
propertyWriter.write(propertyValue);
propertyWriter.close();
System.setProperty("sun.security.smartcardio.library",propertyValue);
break;
}
}
searchProcess.waitFor();
}
else
{
BufferedReader propertyReader = new BufferedReader(new InputStreamReader(new FileInputStream(propertyFile)));
String propertyValue = propertyReader.readLine();
System.setProperty("sun.security.smartcardio.library",propertyValue);
}
} catch (Exception e) {
e.printStackTrace();
}