java.lang.IllegalAccessException: can not access a member of class java.util.Collections$UnmodifiableCollection with modifiers "public"
Asked Answered
M

1

17

I have classes PrimitiveProperty and ComplexProperty and the interface Property. I want to create an implementation of Property which enforces an empty unmodifiable Set of Property instances as return value of Property.getProperties, e.g.

public interface Property {
    Set<Property> getProperties();
}

public class ComplexProperty implements Property {
    private Set<Property> properties; 
    //getter overrides the interface method
}

public class PrimitiveProperty implements Property {
    private final static Set<Property> EMPTY_PROPS = Collections.unmodifiableSet(new HashSet<Property>(1));

    @Override
    public Set<Property> getProperties() {
        return EMPTY_PROPS;
    }
}

With Glassfish 4.0 and I'm getting

java.lang.IllegalAccessException: Class javax.el.ELUtil can not access a member of class java.util.Collections$UnmodifiableCollection with modifiers "public"

when I access the property in the leaf attribute of a Richfaces tree, e.g.

<r:tree id="aTree" 
        toggleType="ajax" var="item" >
  <r:treeModelRecursiveAdaptor roots="#{aCtrl.roots}" 
                               nodes="#{item.properties}" leaf="#{item.properties.isEmpty()}">
    <r:treeNode>
      #{item.name}
    </r:treeNode>
    <r:treeModelAdaptor nodes="#{item.properties}" leaf="#{item.properties.isEmpty()}"/>
  </r:treeModelRecursiveAdaptor>
</r:tree>

The issue disappears if I make the EMPTY_PROPS constant modifiable (by assigning an instance of HashSet instead of the return value of Collections.unmodifiableSet) which is not my intention.

Is it possible to achieve what I want to do? Do I have to invent or use an implementation of what Collections$UnmodifiableCollection ('s subclasses) do(es) which is compatible with the JSF access needs?

Mousey answered 29/7, 2014 at 16:49 Comment(3)
You forgot to show how you're accessing it (in the view side).Cough
I've used the Arrays#asList utility, which returns an unmodifiable List quite frequently, with no problem. Don't know why you have that error for the Set.Improvised
The clue is in the way how he's accessing it.Cough
C
14

Here is the problem:

leaf="#{item.properties.isEmpty()}"

You're attempting to invoke a method directly on the UnmodifiableSet instance. Although it implements Collection, which is public, the UnmodifiableSet implementation itself, where EL (read: Reflection API) is trying to find the method on the class, is not public.

Exactly this problem is reproducible in plain Java (a main() method) as follows:

Set<Object> set = Collections.unmodifiableSet(new HashSet<>());
for (Method method : set.getClass().getMethods()) {
    if ("isEmpty".equals(method.getName())) {
        method.invoke(set); // IllegalAccessException.
    }
}

This is essentially a bug in the EL implementation used.

You'd better just use EL's own empty operator:

leaf="#{empty item.properties}"
Cough answered 30/7, 2014 at 5:46 Comment(13)
... and once more, a JSF abstraction turns out to leak implementation details: to use EL, one should not have to be aware of the specifics of how it interacts with the Reflection API (a public method implemented in a private class can be invoked through reflection by using setAccessible(true) or through the method object of the public super type that declared the method).Scrapple
It's just a bug in EL impl used.Cough
@Cough What would be an approprieate El implementation - I experimented with different versions of javax.el:el-api and org.glassfish.web:el-impl (I'm using and deploying to glassfish)[ocpsoft.org/java/jsf2-java/… and combinations of them, but I guess I don't know what I'm doing nor where to find infos.Mousey
Apache's EL implementation (as used in my Tomcat 7.0.55) doesn't expose this bug. But I wouldn't worry much about this. Just use the empty operator. There it is for.Cough
Is it a bug in EL or in Java? How would you modify your java code above so that is doesn't throw the exception?Neckar
@Maurice: From the answer: "This is essentially a bug in the EL implementation used" and "You'd better just use EL's own empty operator".Cough
@Cough OK. Now I've had the same problem in a different context (not with EL). Again, How would you modify your java code above so that is doesn't throw the exception? If I call set.isEmpty() (or set.size()) in Java, it works without problem. How can we make if work with reflection?Neckar
@Cough (I've tryed method.setAccessible(true);, it doesn't work)Neckar
@Maurice: Oh? Just do instanceof Collection or instanceof Iterable (depending on your coverage) and then simply downcast and perform Collection#isEmpty() or Iterator#hasNext() rather than doing reflection trickery.Cough
@Cough There are times when you need "reflection trickery". Thanks anyway.Neckar
@Maurice: I have yet to find a real world use case where instanceof and downcast (or inherently Class#isAssignableFrom() or Class#isInstance() followed by Class#cast()) is impossible and therefore "reflection trickery" is required.Cough
@Cough I guess your "real world" doesn't include say, an implementation of "EL".Neckar
@Maurice: even in EL the base is readily available in evaluation context. Therefore, "This is essentially a bug in the EL implementation used" and "You'd better just use EL's own empty operator".Cough

© 2022 - 2024 — McMap. All rights reserved.