Instantiating subclass of JNA Structure throws IllegalAccessException - java

I was having trouble getting com.sun.jna.Structure to work, so I tried copying the first JNA test I found and I can't even get that to work.
For convenience, try it online.
import com.sun.jna.Structure;
import java.util.Arrays;
import java.util.List;
public class MyClass {
public void testSimpleSize() throws Exception {
class TestStructure extends Structure {
public int field;
#Override
protected List<String> getFieldOrder() {
return Arrays.asList("field");
}
}
Structure s = new TestStructure();
//assertEquals("Wrong size", 4, s.size());
}
public static void main(String[] args)
{
try
{
new MyClass().testSimpleSize();
}
catch (Exception e)
{
System.out.println(e);
}
}
}
The code compiles ok, but when running I get
Exception in thread "main" java.lang.Error: Exception reading field 'field' in class MyClass$1TestStructure
at com.sun.jna.Structure.getFieldValue(Structure.java:639)
at com.sun.jna.Structure.deriveLayout(Structure.java:1285)
at com.sun.jna.Structure.calculateSize(Structure.java:1159)
at com.sun.jna.Structure.calculateSize(Structure.java:1111)
at com.sun.jna.Structure.allocateMemory(Structure.java:414)
at com.sun.jna.Structure.<init>(Structure.java:205)
at com.sun.jna.Structure.<init>(Structure.java:193)
at com.sun.jna.Structure.<init>(Structure.java:180)
at com.sun.jna.Structure.<init>(Structure.java:172)
at MyClass$1TestStructure.<init>(MyClass.java:8)
at MyClass.testSimpleSize(MyClass.java:15)
at MyClass.main(MyClass.java:23)
Caused by: java.lang.IllegalAccessException: class com.sun.jna.Structure cannot access a member of class MyClass$1TestStructure with modifiers "public"
at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361)
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:591)
at java.base/java.lang.reflect.Field.checkAccess(Field.java:1075)
at java.base/java.lang.reflect.Field.get(Field.java:416)
at com.sun.jna.Structure.getFieldValue(Structure.java:636)
... 11 more
Am I missing something?

As you've discovered in your answer, the root cause of the problem (as indicated by the stack trace) was lack of reflective access to your class. While your answer solves your specific issue, I wanted to expand on more possible reasons this could occur, which may help others.
The reason the JNA test case you linked to works is because the test resides in the same package as the Structure class, which gives the Structure class access to package private classes. In your case, the inner class TestStructure does not have the public modifier, and resides in a different package than com.sun.jna.Structure.
You solved the problem by moving the class to an accessible location, which works. Most often in JNA implementations, structures are declared in interfaces (and thus are by default public).
A second possibility of getting this error is if you are using Java Modules (JPMS). Java Modules do not permit reflective access by default, so it must be explicitly allowed in the module-info.java class, either with opens your.package to com.sun.jna; (to allow runtime-only reflection including private members) or exports your.package to com.sun.jna; (to allow compile-time and run-time public access). While this does not apply in your case (you're using the default package which would be in the unnamed module) this may be something you may encounter in the future if you create a modular project.

I have found a solution and it was much simpler than I thought.
According to the documentation,
An IllegalAccessException is thrown when an application tries to reflectively create an instance (other than an array), set or get a field, or invoke a method, but the currently executing method does not have access to the definition of the specified class, field, method or constructor.
Which means that com.sun.jna.Structure needs to have access to TestStructure, in order to use reflection (which it needs in order to get the struct fields). The simplest way to solve that is to move TestStructure out of the function and make it public. Like this:
import com.sun.jna.Structure;
import java.util.Arrays;
import java.util.List;
public class MyClass {
public class TestStructure extends Structure {
public int field;
#Override
protected List<String> getFieldOrder() {
return Arrays.asList("field");
}
}
public void testSimpleSize() throws Exception {
Structure s = new TestStructure();
//assertEquals("Wrong size", 4, s.size());
}
public static void main(String[] args)
{
try
{
new MyClass().testSimpleSize();
}
catch (Exception e)
{
System.out.println(e);
}
}
}
There may be a better solution, but this is the simplest and works for me.

Related

Java import wildcard accessibility for nested static classes

How does the java accessibility (or perhaps, scope) work with respect to type import multi-level nested classes? An example:
ClassA.java:
package com.oracle.javatests;
public class ClassA {
public static class NestedAA {
public void printSomething() {
System.out.println("inside " + this.getClass().getName());
}
public static class NestedAB{
public void printSomethingAB() {
System.out.println("inside " + this.getClass().getName());
}
}
}
public void printSomething() {
System.out.println("inside " + this.getClass().getName());
}
}
Main.java
package com.oracle.javatests;
import com.oracle.javatests.ClassA.*;
// import com.oracle.javatests.ClassA.NestedAA.*; // Adding this will resolve NestedAB
public class Main {
public static void main (String[] args){
ClassA objA = new ClassA();
objA.printSomething();
NestedAA nestedAA = new NestedAA(); // Ok
NestedAB nestedAB = new NestedAB(); // Compiler error- NestedAB cannot be resolved to a type
}
}
The import statement does not import NestedAB type when using wildcards. A perhaps similar question led me to the java spec sheet which clarifies Type-Import-on-Demand Declarations :
A type-import-on-demand declaration allows all accessible types of a
named package or type to be imported as needed.
The accepted answer to the question implies that the on demand import declarations are not recursive. The reasoning is perhaps what Java considers "all accessible types of a named type", and the general concept of packages but I am falling short of connecting the dots and understand what accessible types means with respect to nested classes.
Can please anyone help explain how the type import and accessibility seem to work in java (while ignoring the arguable use of wildcard imports)
It's not heard to understand. import static com.foo.bar.*; is the exact same thing as import static com.foo.bar.[everything you can imagine here but without dots].
In other words, in your example, with import static pkg.ClassA.*; you can just write NestedAA without qualifiers and that works, because import static pkg.ClassA.NestedAA; would have made that work just the same.
You cannot write NestedAB unqualified and expect that to work; there is nothing you could possibly write instead of a * (which doesn't include dots) that would make that work, therefore, a star import doesn't make it work either.

How do I create n-number exceptions in the same class?

ok, here's my problem.
I want to create my own custom exceptions such as 'UsernameAlreadyTakenException'. I can create a single class in a file which extends Exception. My problem is, I'd rather not have 3 - 5 different files for each 'subclass' that do almost nothing. I'd like to stick them all in the same file. Is this possible and if so how??
You can do this by making a static sub class (though I don't necessarily recommend it). Here's an example:
package com.sandbox;
public class Sandbox {
public static void main(String[] args) throws Exception2 {
throw new Exception2();
}
public static class Exception1 extends Exception {
}
public static class Exception2 extends Exception {
}
public static class Exception3 extends Exception {
}
}
The only restriction against placing multiple classes inside the same file when compiling Java is that only one top-level class in the file can be marked public. This is because public classes must be in a file with the same name, and you can't have multiple classes with the same name.
It's usually a considered a poor practice, and it doesn't guarantee your classes will have the correct protection level, but you can just leave off public when declaring your additional classes. This will give them default protection.
For a good discussion on what default protection means (and the other protection levels), just see this question.
So you could create the file UsernameAlreadyTakenException.java and put in it something like:
public class UsernameAlreadyTakenException extends Exception {
...
}
class MySecondException extends Exception {
...
}
...
class MyNthException extends Exception {
...
}
You could even name the file something else, but then you can't have the class UsernameAlreadyTakenException marked as public. But once again, it is frowned upon to do this. It's usually just better to have a new file for each distinct class.

Is it possible to change buggy class in a closed source library that was not built to support dependency injection?

Say I am using a closed source java library with a known buggy class, say BuggyClass and this class is hardcoded throughout the rest of the library. So I would imagine that the java library looks something like this:
public class BuggyClass {
public T buggyMethod (...) {
// Buggy code here
}
}
with several other classes in the library that make use of this class:
public class Example {
private BuggyClass = new BuggyClass(); // No dependency injection possible
public Example (/* No way to pass in my own subclass of BuggyClass*/) {
// ...
}
}
etc...
Is there any hack, or workaround, possibly using the class loader so that I could subclass BuggyClass and get Example (and every other class in the library that has BuggyClass hardcoded in) to use my subclass?
You can't do a subclass, no, but you can write your own BuggyClass entirely and make sure it appears earlier in the classpath than the real one. I don't think it's documented, but the default classloader seems to typically use the first matching class it finds.
But obviously this is a Really Bad Option, so you want to exhaust every other avenue before trying to solve temporarily work around the underlying problem this way.
Example: Suppose we have this:
// The "buggy" class
package somepackage;
public class BuggyClass {
public String someMethod() {
return "I'm in the buggy class";
}
}
and this:
// Uses the "buggy" class
package somepackage;
public class BuggyClassUser {
public String useBuggyClass() {
BuggyClass c = new BuggyClass();
return c.someMethod();
}
}
compiled and the classes in buggy.jar. Then we have this test class:
import somepackage.*;
public class Test {
public static final void main(String[] args) {
BuggyClassUser u = new BuggyClassUser();
System.out.println(u.useBuggyClass());
}
}
If we run that (*nix format classpath):
java -cp .:buggy.jar Test
...we see
I'm in the buggy class
But if we create a somepackage directory and put this in it:
package somepackage;
public class BuggyClass {
public String someMethod() {
return "I'm in the fixed class"; // <== Difference here
}
}
...and compile that, since we have that in our classpath in front of the jar, this command:
java -cp .:buggy.jar Test
...now gives us this:
I'm in the fixed class
Again, this is very, very much a workaround, not a solution.

Java Inner class shadowing external class

I took the following code from the K&B book "SCJP Sun Certified Programmer for Java 6 Study Guide":
class A { // 1
void m() {
System.out.println("outer");
}
}
public class TestInners {
public static void main(String[] args) {
new TestInners().go();
}
void go() {
new A().m();
class A { // 2
void m() {
System.out.println("inner");
}
}
}
class A { // 3
void m() {
System.out.println("middle");
}
}
}
As stated in the book, this code prints "middle". I infer that the class declaration marked as "3" is shadowing the one marked as "1", which is external to TestInners class.
If the classes were in different packages, I could resolve the ambiguity by qualifying one of them with the package name. But in this case the classes are not only in the same package but in the same file. How can I get an instance of the external class?
I saw the same question here but the accepted answer implies to modify the code adding an enclosing class to the whole thing. My question is how to get the instance using any type of qualifier or reference, if it's even possible.
Assuming your class is in package com.test, all you need to do is use
new com.test.A().m();
using the fully qualified name of the class.
If your classes are in the default package, ie. no package declaration, then you are out of luck and can't access the outer A.
In C++, you can explicitly address global scope by prefixing your symbol with ::, however, Java does not have such a thing.
So if you really want to get the outer A, you have to bite the bullet and do some other sort of enclosure, by for example wrapping it in another class or package.
EDIT: Here is another reason why.
object of innner-A can't be created before defining it.so use new A().m(); after define innner-A inside go() to access inner class object.
void go() {
class A {
void m() {
System.out.println("inner");
}
}
new A().m();
}
to access outer-A you have to append package name,in default package it is impossible to access outer-A.

How to Access package private Class from a Class in some other package?

I have following classses
Hello.java
package speak.hello;
import java.util.Map;
import speak.hi.CustomMap;
import speak.hi.Hi;
public class Hello {
private Hi hi;
Hello(Hi hi) {
this.hi = hi;
}
public String sayHello() {
return "Hello";
}
public String sayHi() {
return hi.sayHi();
}
public Map<String, Object> getMap() {
return hi.getMap();
}
public void clearMap() {
hi.getMap().clear();
}
public void discardMap() {
CustomMap map = (CustomMap) hi.getMap();
map.discard();
}
public static void main(String[] args) {
Hello hello = new Hello(new Hi());
System.out.println(hello.sayHello());
System.out.println(hello.sayHi());
System.out.println(hello.getMap());
hello.clearMap();
System.out.println("--");
hello.discardMap();
}
}
Hi.java
package speak.hi;
import java.util.HashMap;
import java.util.Map;
public class Hi {
public String sayHi() {
return "Hi";
}
public Map<String, Object> getMap() {
return new CustomMap<String, Object>();
}
}
CustomMap.java
package speak.hi;
import java.util.HashMap;
public class CustomMap<K, V> extends HashMap<K, V> {
private static final long serialVersionUID = -7979398843650044928L;
public void discard() {
System.out.println("Discarding Map");
this.clearCache();
this.clear();
}
#Override
public void clear() {
System.out.println("Clearing Map");
super.clear();
}
private void clearCache() {
System.out.println("Clearing Map");
}
}
This works fine until I remove public access specifier from CustomMap
package speak.hi;
import java.util.HashMap;
class CustomMap<K, V> extends HashMap<K, V> {
private static final long serialVersionUID = -7979398843650044928L;
public void discard() {
System.out.println("Discarding Map");
this.clearCache();
this.clear();
}
#Override
public void clear() {
System.out.println("Clearing Map");
super.clear();
}
private void clearCache() {
System.out.println("Clearing Map");
}
}
Compiler yells that
The type speak.hi.CustomMap is not visible
Now If I don't have options to modify speak.hi.CustomMap (third party jar etc..) Is there any way I could still use CustomMap from speak.hello.Hello?
One option that I know is to move speak.hello.Hello to speak.hi.Hello as Now Hello is in package speak.hi it can access package private Class Hi
Is there any other way to do this ? Using reflection perhaps ?
EDIT :Updated with additional details as requested by #StephenC
Is there any other way to do this ? Using reflection perhaps ?
Yes. Reflection can be used to bypass the Java access rules, if your application has full privilege.
For instance, to access a private field of an object from a different class, you need to:
Get the object's Class object.
Use the Class.getDeclaredField(...) method to get a Field object for the field.
Call Field.setAccessible(true) to turn off the access check.
Call Class.getField(object, Field) to get the field's value (or boxed value if it is a primitive type).
If the class itself is not accessible, you need to make sure that you don't refer to the classes identifier in your source code ... 'cos that will result in a compilation error. Instead, assign its reference to (say) variable of type Object or of some other visible supertype, and perform more specific operations on the instance reflectively.
As you might imagine, this is tedious and error prone. You'd be advised to find a better way, like:
getting the suppliers of the classes to fix whatever is causing you to need to break the visibility restrictions,
getting the suppliers of the classes to change their visibility,
finding another way to use the classes that doesn't require breaking open their abstraction, or
ditching them and finding (or writing) something better.
(Generally speaking, if you have to break open an abstraction then something is wrong with either the abstraction itself or the way you are using it.)
Finally, I should add that untrusted code is (should be) run in a security sandbox that blocks the use of the key reflective operations.
Following method Invokes default scoped class method using reflection
public void discardMap() {
//CustomMap map = (CustomMap) hi.getMap();
//map.discard();
try {
Object o =hi.getClass().getMethod("getMap").invoke(hi);
Method m = o.getClass().getMethod("discard");
m.setAccessible(true);
m.invoke(o);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
It may not be possible because:
Class :
Accessible to class from same package?
public : yes
protected : yes
default : yes
private : no
Accessible to class from different package?
public : yes
protected : no
default : unless it is a subclass
private : no
I don't recommend to use non-API classes as they might change in any future version and can break your code.
How did you find out about this class? Is it an Open Source library?
Try to contact the authors of the library, tell them your use case and find a way with them to offer a public API. If it's an open source library you could help them by providing a patch.
Adding this solution for sake of completeness.
One option that I know is to move speak.hello.Hello to speak.hi.Hello as Now Hello is in package speak.hi it can access package private Class Hi
package speak.hi;
public class Hello {
private Hi hi;
Hello(Hi hi) {
this.hi = hi;
}
public String sayHello() {
return "Hello";
}
public String sayHi() {
return hi.sayHi();
}
public static void main(String[] args) {
Hello hello = new Hello(new Hi());
System.out.println(hello.sayHello());
System.out.println(hello.sayHi());
}
}
Not possible. The security model is this : a model to provide security :) If you designed class Hi and delivered it to customers with private access, you wouldn't like them to be able to bypass your restrictions, would you?
I would think that if the authors of a library did not make a particular class part of the public API, it is because they don't want other people using it. You should respect the decision even though you can break it using reflection. Using private API is simply bad programming.
1st class is in package a.b.c.class1 but class1 is private as well as abstract
2nd class is in package a.b.c.class2 extends class1 but class2 is public
3rd class is in package x.y.z.class3
So as to access class1 in class 3 you can write something like:-
Class baseClass = (new class2()).getClass();
and use the instance of its superclass then use:-
baseClass.getSuperClass();
and use it wherever you want.
But then again the baseclass was made abstract and private for a reason hence not advisable to do so but then again this solution could be used as a workaround.
I think by default the class will be "default" (package private, you can say), NOT "private". So it can be accessed with in the same package.
Moreover, you CANNOT make any *top level class Private in Java.
And if you want make a class default and still be able to access it in other package then what will be the purpose of having access specifiers (modifiers) ??
you either need to make class public or move to the same package.
Not Possible you can not create your Hi class as private.

Categories