I made a java program with gradle. I have jdk 15 on my computer but in the gradle file I put :
sourceCompatibility = 1.8
targetCompatibility = 1.8
so it can run with java 8.
I send the jar file to someone that uses java 8 and it works perfectly fine. On my computer, it works perfectly fine with java 8 and java 15.
I send the jar file to another person and he gets the following error :
[ERROR)Exceptioninthread'Thread-7"java.lang.NoSuchMethodError:java.io.ByteArrayOutputStream.toString(Ljava/nio/charset/Charset;)Ljava/lang/String;
[ERROR) at fr.bloomenetwork.fatestaynight.packager.Utils.docxToKsFile(Utils.java:84)
[ERROR) at fr.bloomenetwork.fatestaynight.packager.FetchingThread.run(FetchingThread.java:197)
[ERROR) at java.lang.Thread.run(UnknownSource)
I don't undestand why he gets this error if it works fine for me and the other person.
Here is the code that raise the error :
public static void docxToKsFile(InputStream is, String filename) throws IOException {
ZipInputStream zis = new ZipInputStream(is);
ByteArrayOutputStream fos = new ByteArrayOutputStream();
ZipEntry ze = null;
String xmlContent = null;
while ((ze = zis.getNextEntry()) != null) {
if (ze.getName().equals("word/document.xml")) {
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int len;
while ((len = zis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
xmlContent = new String(fos.toString(StandardCharsets.UTF_8)); //Here is line 84
fos.close();
break;
}
}
fos.close();
String txtContent = xmlContent.replaceAll("</w:p>", "\n");
txtContent = txtContent.replaceAll("<[^>]*/?>", "");
txtContent = txtContent.replaceAll("&", "&");
txtContent = txtContent.replaceAll(""", "\"");
java.nio.file.Files.write(Paths.get(filename), txtContent.getBytes(StandardCharsets.UTF_8));
}
As per the javadoc of ByteArrayOutputStream's toString(Charset) method, it was added in Java10, and is thus not available on JDK8.
sourceCompatibility = 1.8
targetCompatibility = 1.8
so it can run with java 8.
That's not what that means. That merely means the source files are compiled using the JDK8 idea of java-the-language, and that the produces class files are in JDK8 format. It does nothing to guarantee that you are only using the JDK8 idea of the core libraries. Those gradle options are the gradle equivalent of javac's --source resp. --target options.
Those are the wrong options.
With javac, there is now a --release option (introduced in.. I think JDK8?) which covers both source and target compatibility, and even warns about trying to use core library calls that weren't in the stated release. Let's try it, with JDK14 which I happen to have installed on my machine (but should work with JDK11 too):
echo "class Test {{ new java.io.ByteArrayOutputStream().toString(java.nio.charset.StandardCharsets.UTF_8); }}" > Test.java
javac -version
> javac 14.0.1
javac --release 8 Test.java
> Test.java:1: error: no suitable method found for toString(Charset)
javac --release 11 Test.java
> [no message; it compiles fine]
As per the current gradle docs, sourceCompatibility and targetCompatibility are deprecated and should no longer be used. Use options.release = 8 instead, which is the gradle variant of the --release option. Yay!
NB: .toString("UTF-8") is ugly, but does work; it was introduced before java8.
NB2:
On my computer, it works perfectly fine with java 8 and java 15.
Nope. You weren't running it on java8 on your machine. If you had, the app would have failed with the same error. Must have accidentally used the wrong one :P
Did you try this?
xmlContent = new String(fos.toByteArray(), "UTF-8");
Related
javah has been deprecated since JDK 8 and will be/has been removed in JDK 10, and according to JEP 313 and the deprecation text, javac with the -h flag should be used instead:
Warning: The javah tool is planned to be removed in the next major JDK release. The tool has been superseded by the '-h' option added to javac in JDK 8. Users are recommended to migrate to using the javac '-h' option; see the javac man page for more information.
The problem is, javah operates on compiled .class files, while javac operates on source files (i.e. .java files.)
javah works fine with Kotlin and external functions, since everything ends up compiled as Java bytecode, but since there aren't any Java source files when using Kotlin, I don't see any way javac -h could work.
Is there a javah replacement, or a workaround, for Kotlin?
I recommend gjavap.
In the future I will also implement an easier-to-use command line tool which provides similar functionality to javap.
There is currently no built-in way to do this. There is an open issue for this on the Kotlin issue tracker that was raised in November 2019, but as of now it has not been prioritized and doesn't have a target version.
The only way to produce the header with JDK 10+ is to use javac -h, which only works for Java source code, not Kotlin. I tested the method in How to solve missing javah in Java 10 – ugly way linked by Oo.oO, and it works as a workaround for now. The steps are:
Use javap to decompile the bytecode back into Java
Edit the decompiled Java to make it suitable for producing the header file (reducing the file to just the native method definitions).
Run the decompiled Java code through javac -h to produce the header
I'm thinking of writing a gradle script to do this (or hoping somebody else beats me to it!). If I manage to get it done, I'll update this post.
I've wrote a simple gradle task for generating JNI headers, its similar approach as the one posted by #Oo.oO but has a better integration with gradle and hence can be run on Windows as well as Unix based OS.
val generateJniHeaders by tasks.creating {
group = "build"
dependsOn(tasks.getByName("compileKotlinJvm"))
// For caching
inputs.dir("src/jvmMain/kotlin")
outputs.dir("src/jvmMain/generated/jni")
doLast {
val javaHome = Jvm.current().javaHome
val javap = javaHome.resolve("bin").walk().firstOrNull { it.name.startsWith("javap") }?.absolutePath ?: error("javap not found")
val javac = javaHome.resolve("bin").walk().firstOrNull { it.name.startsWith("javac") }?.absolutePath ?: error("javac not found")
val buildDir = file("build/classes/kotlin/jvm/main")
val tmpDir = file("build/tmp/jvmJni").apply { mkdirs() }
val bodyExtractingRegex = """^.+\Rpublic \w* ?class ([^\s]+).*\{\R((?s:.+))\}\R$""".toRegex()
val nativeMethodExtractingRegex = """.*\bnative\b.*""".toRegex()
buildDir.walkTopDown()
.filter { "META" !in it.absolutePath }
.forEach { file ->
if (!file.isFile) return#forEach
val output = ByteArrayOutputStream().use {
project.exec {
commandLine(javap, "-private", "-cp", buildDir.absolutePath, file.absolutePath)
standardOutput = it
}.assertNormalExitValue()
it.toString()
}
val (qualifiedName, methodInfo) = bodyExtractingRegex.find(output)?.destructured ?: return#forEach
val lastDot = qualifiedName.lastIndexOf('.')
val packageName = qualifiedName.substring(0, lastDot)
val className = qualifiedName.substring(lastDot+1, qualifiedName.length)
val nativeMethods =
nativeMethodExtractingRegex.findAll(methodInfo).mapNotNull { it.groups }.flatMap { it.asSequence().mapNotNull { group -> group?.value } }.toList()
if (nativeMethods.isEmpty()) return#forEach
val source = buildString {
appendln("package $packageName;")
appendln("public class $className {")
for (method in nativeMethods) {
if ("()" in method) appendln(method)
else {
val updatedMethod = StringBuilder(method).apply {
var count = 0
var i = 0
while (i < length) {
if (this[i] == ',' || this[i] == ')') insert(i, " arg${count++}".also { i += it.length + 1 })
else i++
}
}
appendln(updatedMethod)
}
}
appendln("}")
}
val outputFile = tmpDir.resolve(packageName.replace(".", "/")).apply { mkdirs() }.resolve("$className.java").apply { delete() }.apply { createNewFile() }
outputFile.writeText(source)
project.exec {
commandLine(javac, "-h", jniHeaderDirectory.absolutePath, outputFile.absolutePath)
}.assertNormalExitValue()
}
}
}
Until Kotlin starts generating JDK10-specific bytecode, you can use javah tool from JDK 9 or lower on the compiled kotlin classes.
And even after that you can compile external functions with jvmTarget=1.8 and use javah on the resulting classes.
You can also make it using javap and creating facade classes based on class files.
Take a look here for a sample: http://www.owsiak.org/how-to-solve-missing-javah-ugly-way/
After some problems with differences between JSE versions, I'm trying to log the Java compiler version used to compile (it's Groovy 2.1.9, Grails 2.3.8, Java 1.7.0_60 in fact).
After some rummaging around, I've constructed this piece of code to read the leading bytes of the class - see /http://en.wikipedia.org/wiki/Java_class_file#General_layout
(change the path to the class to match the package name):
class CompilerVersionSupport {
public static String getVersion() {
String classAsPath = 'com/my/organisation/CompilerVersionSupport.class';
InputStream stream = (new CompilerVersionSupport()).getClass().getClassLoader().getResourceAsStream(classAsPath);
DataInputStream ins = new DataInputStream (stream)
assert( ins.readUnsignedShort() == 0xcafe )
assert( ins.readUnsignedShort() == 0xbabe )
int minor = ins.readUnsignedShort();
int major = ins.readUnsignedShort();
ins.close();
int javaVersion = major - 44
return "1.$javaVersion"
}
}
Trouble is, it returns 1.5.
What could be going on?
Charles
The default Groovy behaviour is not to compile the code with the same bytecode version as the JDK being used. 1.5 is the default for compatibility reasons, IMHO. If you want the compiler to output newer bytecode you need to set that explicitly.
For example if you're using Maven to compile the code, you can use the GMavenPlus plugin. See the description of the targetBytecode parameter.
If you're not using Maven you can use -Dgroovy.target.bytecode=1.7 or research the possibilities for your particular build tool
If you're using Maven as the build tool, then chances are that it's using the gmavenplus-plugin to compile Groovy. To find out the target Java version of the bytecode generated I poked into the pom of the gmavenplus-plugin that my application uses: ~/.m2/repository/org/codehaus/gmavenplus/gmavenplus-plugin/1.5/gmavenplus-plugin-1.5.pom.
Inside that file I saw this, notice <javaVersion/>,
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<mavenVersion>2.2.1</mavenVersion>
<coberturaPluginVersion>2.7</coberturaPluginVersion>
<javadocPluginVersion>2.10.1</javadocPluginVersion>
<!-- these are properties so integration tests can use them -->
<javaVersion>1.5</javaVersion>
<dependencyPluginVersion>2.10</dependencyPluginVersion>
<compilerPluginVersion>3.2</compilerPluginVersion>
<junitVersion>4.12</junitVersion>
<surefirePluginVersion>2.18.1</surefirePluginVersion>
<pluginPluginVersion>3.4</pluginPluginVersion>
<!-- this is a property so that site generation can use it -->
<sourcePluginVersion>2.4</sourcePluginVersion>
<!-- this is a property so that site generation and integration tests can use it -->
<groovyVersion>2.4.1</groovyVersion>
</properties>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compilerPluginVersion}</version>
<configuration>
<source>${javaVersion}</source>
<target>${javaVersion}</target>
</configuration>
</plugin>
I use IntelliJ for an IDE. IntelliJ is automatically setting the language level to Java 1.5. Even if I change it, when I re-import projects it resets back to Java 1.5 (I've fuzzed out sensitive information),
I think the issue is with the program you are using to find the class version. If the assertion is not enabled the stream doesnt read the first two unsigned shorts and hence the subsequent minor and major read statements results in 0Xcafe and 0xbabe respectively. Try enabling assertion or try using an if check.
public static String getVersion() throws Exception {
String classAsPath = "com/my/organisation/CompilerVersionSupport.class";
InputStream stream = (new CompilerVersionSupport()).getClass().getClassLoader().getResourceAsStream(classAsPath);
DataInputStream ins = new DataInputStream(stream);
if(ins.readUnsignedShort() != 0xcafe) throw new AssertionError("Invalid Class");
if(ins.readUnsignedShort() != 0xbabe) throw new AssertionError("Invalid Class");
int minor = ins.readUnsignedShort();
int major = ins.readUnsignedShort();
ins.close();
int javaVersion = major - 44;
return "1." + javaVersion;
}
I'm trying to create JVM usin JNI. I'm using win 7 64 bits OS. On line JNI_CreateJavaVM my program crashes. I decided to compile my program using 64 bit compiler and got following error:
Error 1 error LNK2001: unresolved external symbol __imp_JNI_CreateJavaVM
Where is the point I should start to look for linking problem and why my program crashes in 32 bit mode?
void createJVM()
{
JavaVMInitArgs vm_args;
JavaVMOption options[4];
int n = 0;
char * str;
str= new char[1000];
sprintf(str, "-Djava.class.path=%S\\tst.jar", myPath);
options[n++].optionString = str;
str= new char[1000];
sprintf(str, "-Djava.library.path=%S\\lib;%S", myPath, myPath);
options[n++].optionString = str;
str= new char[1000];
sprintf(str, "-Duser.dir=%S", myPath);
options[n++].optionString = str;
vm_args.version = JNI_VERSION_1_4;
vm_args.nOptions = n;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
}
have you added 'jvm.lib' as Additional Dependency in your project?
furthermore, you need to specify the location of jvm.lib in Additional Library Directories...
also please note that for 64-bit application, you would need to point to the 64-bit library, otherwise linker won't link
You can find those settings in the Configuration Properties->Linker area.
hope this information helps you out.
Cheers,
Since I can't wote up (still less than 15 reputation) I just want to confirm that Naytzyrhc solution worked for me.
Just to clarify it a bit more, in Visual Studio Express 2013 (v12) you should go to:
Project -> [YourProjectName] Properties... -> Linker -> General -> Additional Library Directories
for adding lib folder to additional library directories, and:
Project -> [YourProjectName] Properties... -> Linker -> Input -> Additional Dependencies
for adding jvm.lib to additional dependencies.
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.
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
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
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
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
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.
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.
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();
}
i am using java se7 on mac, the oracle preview.
My problem is that "Files.probeContentType" returns null...is it possible that its due to the early status of se7 for mac?
My code:
if(directory == null) return;
String content = null;
try {
content = Files.probeContentType(directory.toPath());
} catch (IOException e) {
JOptionPane.showMessageDialog(main, e.toString());
return;
}
if(content == null)
{
return;
}
else if(content.contains("image"))
{
main.pctviewer.setImage(directory);
}
the name of the file is:
"/Users/admin/Desktop/temp/q12/formulare/Bildschirmfoto 2012-09-11 um 17.57.59.png"
and in debug mode in eclipse if i hover above File "file path = Unis-path(id:145)" is red
I have reported the bug to oracle again, hoping they will backport the jdk8 solution (I don't have much hope but you never know).
In the meantime you can use my own backport of the FileTypeDetector available at https://github.com/jeantil/jdk7-mimeutils the maven project packages to a jar which can be added to your classpath to enable mime type detection. I also provide a mime.types file to put in your home folder for the detection to work correctly. I extracted the mime.types file from some version of apache so it's pretty complete.
I found that the FileTypeDetector is buggy on OS X: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7133484
Apparently this will be fixed in Java 8.
Use the below approach to get mime type of file in Java 8 :
String location = "/Users/user/Desktop/leaf.jpg";
File file = new File(location);
Path source = Paths.get(location);
MimetypesFileTypeMap m = new MimetypesFileTypeMap(source.toString());
System.out.println( m.getContentType(file) );
Output of the above code is : 'image/jpg'