Javassist NotFoundException when getting java.io.Serializable with JDK9 - javassist

I have the following code:
private static CtClass resolveCtClass(String clazz) throws NotFoundException {
ClassPool pool = ClassPool.getDefault();
return pool.get( clazz );
}
When running under JDK8, if this method is called using java.io.Serializable, it works, but when running under the JDK9 environment, it throws the NotFoundException.
Is there something I overlooked here?

This does no longer happen with the current EA builds of Java 9. Class files are now always locatable even if they are encapsulated in a module.
This is a consequence of Java 9's module encapsulation where non-exported resources are no longer available via the ClassLoader API. Under the covers, Javassist calls
ClassLoader.getSystemClassLoader().findResource("java/io/Serializable.class");
to get hold of the class file for Serializable. It then parses this class file and represents the information similarly to the Java reflection API but without loading the class such that it can be edited prior to loading it.
Until Java 8, this class file was accessible as most class loaders rely on looking up a class file before loading it such that the above call returned a URL pointing to the file. Since Java 9, resources of named modules are only available via the new API method findResource(String, String) where the second arguments names the module of that class.
The short answer is: Javassist does no longer work with Java 9 and none of its dependant projects will. This is a known issue with the current Java 9 implementation and will hopefully be fixed prior to release.

(I never used Javassist so I'm just shooting in the dark, here...)
The documentation of ClassPool says:
If get() is called on this object, it searches various sources represented by ClassPath to find a class file and then it creates a CtClass object representing that class file.
This seems to be bound to the concept of the class path. Looking at ClassPath and CtClass supports that assumption.
If that is the case, then Javassist might just not be fit to look into JDK 9's brand new modules.
If my guess is correct, you should not be able to get any JDK class from the pool. This should be easily verifiable.

Related

Kotlin 1.7 dependency resolution

Assume I have gradle mudule structure like that: module1 => module2 => gson.
Module2 exposes gson as a return type in one of its public interfaces' methods but it's never used in module1. The dependencies are provided using
implementation
configuration. the question is should I provide gson dependency to module1 considering it's not used there or not? is there any standard for this? I'm asking because in kotlin 1.6.10 it works fine but kotlin 1.7.20 seems to break it and during dagger2 processing step I get an error like this:
ComponentProcessingStep was unable to process
'module1.MyComponent' because
'Gson'
could not be resolved.
This is what an api (instead of implementation) dependency is for in gradle. Just replace implementation("gson:...") with api("gson:...")
See https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation
So when should you use the api configuration? An API dependency is one that contains at least one type that is exposed in the library binary interface, often referred to as its ABI (Application Binary Interface). This includes, but is not limited to:
types used in super classes or interfaces
types used in public method parameters, including generic parameter types (where public is something that is visible to compilers. I.e. , public, protected and package private members in the Java world)
...
The latter is your use case.
As for why dagger didn't complain in 1.6, I wouldn't know, in any case it was wrong in 1.6 as well, you just got lucky that nothing tripped over it.

class Test (in unnamed module #0x33f88ab) cannot access class com.sun.media.sound.FFT (in module java.desktop)

I want to use musicg to analysis audio fingerprint.
But I got following error in musicg library.
IllegalAccessError: class Test
(in unnamed module #0x33f88ab) cannot access class com.sun.media.sound.FFT
(in module java.desktop)
because module java.desktop does not export com.sun.media.sound to unnamed module #0x33f88ab
What should I do?
Environment
Kotlin
JDK 17(downgrade available)
musicg 1.4.2.2
My code
fun main(args: Array<String>) {
FFT(10, 20)
}
got exception in FFT(10, 20)
com.sun and its sub-packages are not part of the public Java API. They implement some standard Java APIs, but you shouldn't refer to them directly. (They're likely to change and/or be renamed or removed between JVM releases, and non-Sun/Oracle JVMs probably won't have them at all.)
In most cases you should access the public API classes (e.g. in javax.sound) instead. (Those may use sun.*/com.sun.*/etc. classes internally as needed, but that's merely an implementation detail.)
In early versions of Java, there was nothing to stop people using those internal implementation classes, and so some developers got into bad habits. But Java 9 added a module system, which restricts access to them. The error message you see is a result of that.
The details are in JEP 260. The intent was that there would be public APIs for all of the critical APIs that were being restricted. However, according to this Oracle forum page, the work wasn't completed, and so there are some internal APIs for which no public equivalent exists yet.
FFT looks like one of those classes that has been overlooked. I can't see a direct replacement for it, I'm afraid. Is there a third-party library you can use? This question gives some options.

How to access top level functions referring to the file they're declared in?

If you have a file MyUtils.kt in app/utils/:
package app.utils
fun log(message: String) {
println(message)
}
And you want to access this log() function from another file App.kt at app/, you will do this:
package app
import app.utils.log
fun main() {
log("hey")
}
But I don't like how the log() function is imported from the /utils package and not from the MyUtils.kt file explicitly.
One alternative would be to declare MyUtils.kt with package app.utils.MyUtils but I don't think it's a good practice to declare files in packages that don't have a matching folder.
Is there a way around this?
Edit: declaring an object or class wouldn't be a good solution either because of memory issues.
TL;DR
You can't.
Long answer
You seem to have a misconception that a class or object somehow adds some memory issues to your application.
That's not the case.
In fact, if you're running on the JVM, your log function will compile to the following:
public final class UtilsKt {
public static final void log(#NotNull String message) {
Intrinsics.checkParameterIsNotNull(message, "message");
System.out.println(message);
}
}
You can hit Meta+Shift+A on IntelliJ, then do Show Kotlin bytecode if you don't believe me.
Also, you seem to believe it should be possible to refer to a file name explicitly in a Kotlin import. There isn't a way to do that. Files have almost no bearings on the Kotlin compiled-code (it's kind of an accident that the file name actually reflects on the generated Java class name, as shown above).
Packages in Kotlin are usually arranged to meet the directories they are in... but that's not mandatory, by the way. You can write classes in several packages under the same directory. This means that the file names and even directory names do not really affect Kotlin runtime types and imports.
If you are worried about not being able to quickly find out where your functions are declared, I suggest you use the Java convention of calling the files by the name of the class that live in it, and then wrap all functions into an object (there's absolutely no runtime costs associated with that).
package app.utils
object MyUtils {
fun log(message:String) => println(message)
}
File: app/utils/MyUtils.kt
But notice that with any decent IDE, navigating to the declaration of a function is trivial (Meta+B in IntelliJ, usually) regardless of in which file it is in, so this problem is not normally an issue when you work with an IDE.

Define a missing method through AOP?

I'm in a situation where the implementation of a library we are using is newer than the implementation one of our dependencies was coded against. E.g. Dependency uses MyLibrary-1.0 and we're using MyLibrary-2.0.
In the newer implementation a deprecated method has been removed, which causes run-time errors for us.
I'm trying to use AOP (Spring-AOP to be specific) to intercept calls made to the missing method, and proxy them into an existing method... but I can't seem to get the Aspect right.
It feels like Java is raising the 'java.lang.NoSuchMethodError' exception before my Aspect has an opportunity to intercept. Is there some trick I'm missing, or is this just not feasible (e.g. the method must exist in order to aspect it)?
#Before("execution(* com.mylibrary.SomeClass.*(..))")
Fails with java.lang.NoSuchMethodError
#Around("target(com.mylibrary.SomeClass) && execution(* missingMethod(..))")
Fails with java.lang.NoSuchMethodError
Assuming that your are talking about a 3rd party library which is independent of Spring, you cannot use Spring AOP with its proxy-based "AOP lite" approach which only works for public, non-static methods of Spring components. Please use the more powerful AspectJ instead. The Spring manual explains how to integrate full AspectJ with load-time weaving (LTW) into Spring applications. If your application is not based on Spring so far and you just wanted to use the framework because of Spring AOP, you can skip the whole Spring stuff altogether and use plain AspectJ.
The feature you want to use is an inter-type declaration (ITD), more specifically AspectJ's ability to declare methods for existing classes. Here is some sample code:
3rd party library:
package org.library;
public class Utility {
public String toHex(int number) {
return Integer.toHexString(number);
}
// Let us assume that this method was removed from the new library version
/*
#Deprecated
public String toOct(int number) {
return Integer.toOctalString(number);
}
*/
}
Let us assume that the method I commented out was just removed from the latest version your own project depends on, but you know how to re-implement it.
Project dependency depending on old version of 3rd party library:
package com.dependency;
import org.library.Utility;
public class MyDependency {
public void doSomethingWith(int number) {
System.out.println(number + " in octal = " + new Utility().toOct(number));
}
}
Because the previously deprecated method Utility.toOct does not exist anymore in the version used by your own project, you will get NoSuchMethodError during runtime when calling MyDependency.doSomethingWith.
Your own application:
package de.scrum_master.app;
import org.library.Utility;
import com.dependency.MyDependency;
public class Application {
public static void main(String[] args) {
System.out.println("3333 in hexadecimal = " + new Utility().toHex(3333));
new MyDependency().doSomethingWith(987);
}
}
As you can see, the application also uses the same library, but a different method which still exists in the current version. Unfortunately, it also uses the dependency which relies on the existence of the removed method. So how should we repair this?
Aspect using ITD:
AspectJ to the rescue! We just add the missing method to the 3rd party library.
package de.scrum_master.aspect;
import org.library.Utility;
public aspect DeprecatedMethodProvider {
public String Utility.toOct(int number) {
return Integer.toOctalString(number);
}
}
If you compile this project with the AspectJ compiler Ajc, it just works. In your real life scenario, compile the aspect into its own aspect library, put the weaving agent aspectjweaver.jar on the JVM command line in order to activate LTW and enjoy how it weaves the method into the library class via byte code instrumentation during class-loading.
Log output:
3333 in hexadecimal = d05
987 in octal = 1733
Et voilà! Enjoy. :-)
When the JVM load a class, it resolves all dependencies in a "linker" phase : external classes, properties and method. You can't pass this phase in your case, because methods are missing.
There are two modes on (Spring-)AOP: Proxy, and weaving.
Proxy create... a proxy around a class: the targeted class must exist and be loaded
Weaving can happen before a class is loaded: when a classloader load a class, an array of byte[] is passed to the weaver, which can manipulate the class bytecode before the class is really reified. This type of aop can work in your case. However, it will not be an easy task.

Where is Java Main method defined in source?

Pardon me for asking this silly question. Where can i find java main method definition in java source? it is not in object class or system class? so how and where is it defined exactly?
public static void main(String[] args) {}
Where is Java Main method defined in source?
It is declared in a class. Conventionally, it is a top-level (i.e. non-nested) public class, but that is not a requirement. (A non-public class will work, and I think a static nested class will work too.)
How do you find the main method?
Use grep or similar to search the source code of your application.
Use your IDE's method search capability.
Read the application's user documentation or launch script.
Look for the main method in the index of the application's javadoc.
How does the java command find it?
It doesn't! You specify the fully qualified class name of the class containing the main method you want to use on the java command line. Alternatively, you can set the Main-Class attribute in a JAR file's manifest so that the user doesn't need to know the class name.
UPDATE - If you are looking for the code in the OpenJDK source tree that loads the entrypoint class, finds the main method and invokes it, it is all in "jdk8u/jdk/src/share/bin/java.c". Happy reading.
It's not defined anywhere as code (in the standard libraries).
The JVM expects to find it if you're running a class, and if it's not found you get an error. Therefore it's up to you to create a public static void main(String[] args) method if you want to run your class.
main method is the entry point of an application in java. All the java classes are packaged as libraries which will be used in any application. So class files are used as references instead of separte executable. You can't execute the java source code separately because there won't be any main method definition in java source code.