Виртуальная песочница (тм)

Friday, January 11, 2013

Что делать с "java.lang.IllegalAccessException: Class sun.reflect.misc.Trampoline can not access a member of class" или несколько детских граблей при работе с JavaBeans

Допустим, вы не каждый день пишете свои собственные JavaBeans, a тут вдруг польстились на возможность класса XMLEncoder легко и непринужденно сериализовать объекты в XML и решили написать небольшой proof-of-concept примерчик, чтобы удостовериться, что оно все работает, как обещано. С кем, в конце концов, не бывает...

"Если с первого раза не получилось, парашютный спорт не для вас..." При попытке запуситть наш простой примерчик мы получаем в консоли следующее послание от компилятора:

java.lang.IllegalAccessException: Class sun.reflect.misc.Trampoline can not access a member of class Ххх with modifiers ""
Continuing ...

Вопрос: за что и что нам с этим делать?

Solution: make the class of the JavaBean public. :)


Пример #1, показывающий, что класс JavaBean нужно делать публичным

import java.beans.XMLEncoder;
import java.io.*;
public class RakeJB_A {
public static void main(String[] args) throws FileNotFoundException {
new RakeJB_A().test();
}
private void test() throws FileNotFoundException {
MyBean b = new MyBean();
XMLEncoder e = new XMLEncoder( new BufferedOutputStream( new FileOutputStream( getClass().getName() + ".XML") ) );
e.writeObject(b);
e.close();
}
}
class MyBean {
private int n;
public void setN(int n) { this.n = n; }
public int getN() { return n; }
private String s;
public void setS(String s) { this.s = s; }
public String getS() { return s; }
}
/*
Outputs
java.lang.IllegalAccessException: Class sun.reflect.misc.Trampoline can not access a member of class MyBean with modifiers ""
Continuing ...
java.lang.Exception: XMLEncoder: discarding statement XMLEncoder.writeObject(MyBean);
Continuing ...
because the bean class is not public.
Contents of the XML file:
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_11" class="java.beans.XMLDecoder">
</java>
*/
view raw RakeJB_A.java hosted with ❤ by GitHub



Пример #2, в котором полное счастье так и не наступает

Хорошо, мы выносим класс бина в отдельный файл и делаем его публичным, и он даже компилируется:

public class MyBean_B {
private int n;
public void setN(int n) { this.n = n; }
public int getN() { return n; }
private String s;
public void setS(String s) { this.s = s; }
public String getS() { return s; }
}
view raw MyBean_B.java hosted with ❤ by GitHub

После чего тем, что осталось, пытаемся сериализовать его в XML:

import java.beans.XMLEncoder;
import java.io.*;
public class RakeJB_B {
public static void main(String[] args) throws FileNotFoundException {
new RakeJB_B().test();
}
private void test() throws FileNotFoundException {
MyBean_B b = new MyBean_B();
XMLEncoder e = new XMLEncoder( new BufferedOutputStream( new FileOutputStream( getClass().getName() + ".XML") ) );
e.writeObject(b);
e.close();
}
}
view raw RakeJB_B.java hosted with ❤ by GitHub

В итоге в консоли чисто, а в текущем каталоге мы получаем такой файлик:

<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_11" class="java.beans.XMLDecoder">
<object class="MyBean_B"/>
</java>
view raw RakeJB_B.XML hosted with ❤ by GitHub

Уже гораздо приятнее - мы видим ссылку на объект класса MyBean_B, но хотелось бы какого-то упоминания свойств этого объекта... Может быть инициализация поможет?

public class MyBean_C {
private int n = 123;
public void setN(int n) { this.n = n; }
public int getN() { return n; }
private String s = "test";
public void setS(String s) { this.s = s; }
public String getS() { return s; }
}
view raw MyBean_C.java hosted with ❤ by GitHub

Нет, не поможет, дело в том, что XMLEncoder сравнивает значения свойств со значениями по умолчанию (сразу после создания объекта), и если они совпадают, то не сериализуются. Действительно - зачем?


Пример #3, в котором уже почти хорошо

Добавим для разнообразия в наш бин немножечко BigDecimal:
import java.math.BigDecimal;
public class MyBean_D {
private int n = 123;
public void setN(int n) { this.n = n; }
public int getN() { return n; }
private String s = "test";
public void setS(String s) { this.s = s; }
public String getS() { return s; }
private BigDecimal d = BigDecimal.ONE;
public void setD(BigDecimal d) { this.d = d; }
public BigDecimal getD() { return d; }
}
view raw MyBean_D.java hosted with ❤ by GitHub

И даже присвоим ему какое-нибудь значение не по умолчанию:

import java.beans.XMLEncoder;
import java.math.BigDecimal;
import java.io.*;
public class RakeJB_D {
public static void main(String[] args) throws FileNotFoundException {
new RakeJB_D().test();
}
private void test() throws FileNotFoundException {
MyBean_D b = new MyBean_D();
b.setN( (int) System.currentTimeMillis() );
b.setS( String.valueOf( System.currentTimeMillis() ) );
b.setD( new BigDecimal( System.currentTimeMillis() ) );
XMLEncoder e = new XMLEncoder( new BufferedOutputStream( new FileOutputStream( getClass().getName() + ".XML") ) );
e.writeObject(b);
e.close();
}
}
view raw RakeJB_D.java hosted with ❤ by GitHub

String - видим, int - видим, BigDecimal - не видим:

<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_11" class="java.beans.XMLDecoder">
<object class="MyBean_D">
<void property="n">
<int>972855620</int>
</void>
<void property="s">
<string>1358182521156</string>
</void>
</object>
</java>
view raw RakeJB_D.XML hosted with ❤ by GitHub

Очень жаль...


Пример #4, в котором Kuldeep.Oli избавляет нас от страданий, поведав о java.beans.PersistenceDelegate


import java.beans.XMLEncoder;
import java.math.BigDecimal;
import java.io.*;
public class RakeJB_E {
public static void main(String[] args) throws FileNotFoundException {
new RakeJB_E().test();
}
private void test() throws FileNotFoundException {
MyBean_D b = new MyBean_D();
b.setN( (int) System.currentTimeMillis() );
b.setS( String.valueOf( System.currentTimeMillis() ) );
b.setD( new BigDecimal( System.currentTimeMillis() ) );
XMLEncoder e = new XMLEncoder( new BufferedOutputStream( new FileOutputStream( getClass().getName() + ".XML") ) );
e.setPersistenceDelegate( BigDecimal.class, e.getPersistenceDelegate( Long.class ) );
e.writeObject(b);
e.close();
}
}
/*
Creates an XML file like this:
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_11" class="java.beans.XMLDecoder">
<object class="MyBean_D">
<void property="d">
<object class="java.math.BigDecimal">
<string>1358183392478</string>
</object>
</void>
<void property="n">
<int>973726942</int>
</void>
<void property="s">
<string>1358183392478</string>
</void>
</object>
</java>
*/
view raw RakeJB_E.java hosted with ❤ by GitHub


Ссылки по теме:
JavaBeans Trail in The Java Tutorials from Sun Oracle.
Javadoc for class java.beans.XMLEncoder

No comments: