Весна, NotReadablePropertyException и версия Glassfish

Я работаю над веб-приложением, которое использует Spring MVC.

он отлично работает на Glassfish 3.0.1, но при переходе на Glassfish 3.1 он начал действовать странно. Некоторые страницы показывают только частично или вообще ничего не показывают, а в журнале много сообщений такого типа:

    [#|2012-08-30T11:50:17.582+0200|WARNING|glassfish3.1|javax.enterprise.system.container.web.com.sun.enterprise.web|_ThreadID=69;_ThreadName=Thread-1;|StandardWrapperValve[SpringServlet]: PWC1406: Servlet.service() for servlet SpringServlet threw exception
    org.springframework.beans.NotReadablePropertyException: Invalid property 'something' of bean class [com.something.Something]: Bean property 'something' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
        at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:729)
        at org.springframework.beans.BeanWrapperImpl.getNestedBeanWrapper(BeanWrapperImpl.java:576)
        at org.springframework.beans.BeanWrapperImpl.getBeanWrapperForPropertyPath(BeanWrapperImpl.java:553)
        at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:719)
        at org.springframework.validation.AbstractPropertyBindingResult.getActualFieldValue(AbstractPropertyBindingResult.java:99)
        at org.springframework.validation.AbstractBindingResult.getFieldValue(AbstractBindingResult.java:226)
        at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:120)
        at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:178)
        at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:198)
        at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java:164)
        at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.java:127)
        at org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeDefaultAttributes(AbstractHtmlElementTag.java:421)
        at org.springframework.web.servlet.tags.form.TextareaTag.writeTagContent(TextareaTag.java:95)
        at org.springframework.web.servlet.tags.form.AbstractFormTag.doStartTagInternal(AbstractFormTag.java:102)
        at org.springframework.web.servlet.tags.RequestContextAwareTag.doStartTag(RequestContextAwareTag.java:79)

сообщение об ошибке не является неправильным, потому что рассматриваемое свойство не имеет метода setter (получает свое значение через конструктор). Но как я уже сказал, это не было проблем при использовании Glassfish 3.0.1, только при использовании его на новом сервере с Glassfish 3.1.

кто-нибудь знает, есть ли что-то в версии Glassfish, что может вызвать это? Или это какая-то конфигурация, которая отсутствует на новом сервере?

код:

:
@ModelAttribute
public SomethingContainer retriveSomethingContainer(@PathVariable final long id {
    return somethingContainerDao.retrieveSomethingContainer(id);       
}

@InitBinder("somethingContainer")
public void initBinderForSomething(final WebDataBinder binder) {
    binder.setAllowedFields(new String[] {
        "something.title",
        "something.description",
    });
}

SomethingContainer:

@Embedded
private final Something something = new Something();

public Something getSomething() {
    return something;
}
//no setter

public String getDescription() {
    return something.getDescription();
}

обновление:

перезапуск Glassfish фактически устраняет проблему - временно. Я подозреваю, что это может быть связано с загрузкой пользовательских связующих, у нас были некоторые проблемы с ошибками из памяти, которые, как я думал, имели к этому какое-то отношение, но это было исправлено без исправления этой проблемы.

обновление 2:

на сервере 3.0.1 одним из аргументов jvm был-client. На 3.1-сервер, это сервер. Мы изменили его на-client, и это заставило частоту ошибки сильно снизиться, это происходило каждый другой день с-server, потребовалось 2 недели, чтобы это произошло с-client.

обновление 3:

некоторая информация о серверах (больше можно добавить по запросу..)

Server1 (рабочий):

Windows Server 2003
Java jdk 6 build 35
Glassfish 3.0.1 build 22
-xmx 1024m

Сервер2 (проблемы):

Windows Server 2008 64-bit
Java jdk 6 build 31
Glassfish 3.1 build 43
-xmx 1088m
-xms 1088m

мы используем весеннюю версию 3.1.0.

обновление 4:

я воссоздал ошибку, переименовав поле в jsp в то, что не существует в modelattribute.

но, что более важно, я заметил кое-что: поля, в которых система не может найти геттеров, часто являются полями суперклассов тех, на которые ссылаются в modelattribute. Чтобы продолжить мой пример, SomthingContainer действительно таков:

public class SuperSomethingContainer {
    [...]
    private Something something;
    public Something getSomething() {
        return something;
    }
}

public class SomethingContainer extends SuperSomethingContainer {
    [...]
}

ссылка в контроллере остается как есть, поэтому она ссылается на поле, которое находится в суперклассе рассматриваемого объекта.

обновление 5:

Я попытался подключиться к производственный сервер с отладчиком после возникновения ошибки. Я поставил точку останова в операторе return метода контроллера, возвращающего объект с ошибкой, и попытался увидеть, могу ли я получить доступ к полю с проблемами в то время. И что я мог, поэтому проблема должна лежать в Spring MVC / сгенерированных JSP-классах.

(кроме того, поле по ошибке было типа "someobject.что-то[0].somethingelse[0]", но когда список somethingelse был пуст, ошибки не было! Для меня это подразумевает, что он как-то не может найти get-метод списка(?))

обновление 6:

кажется, что проблема связана с генерацией Java-классов из jsps. Мы не использовали precompile jsps при развертывании, поэтому они компилируются при первом использовании. Проблема возникает при первом посещении страницы и компиляции jsp. Я также заметил, что как только проблема возникла, jsps, которые компилируются после, будут давать ошибки. Я сохранил некоторые проблемы, порожденные java-файлы, и при следующем перезапуске я буду сравнивать их с рабочими. Все ближе :)

обновление 7:

сравнил скомпилированные JSP java файлы, которые привели к ошибке с теми, которые не сделали, и не было никакой разницы. Так что это вроде как оставляет это.

Итак, теперь я знаю, что объект Java, покидающий контроллер, в порядке (проверен отладчиком), и класс java, сгенерированный из jsp, в порядке. Так что это должно быть что-то среднее, теперь мне нужно ... чтобы выяснить что...

обновление 8:

еще один раунд отладки и сузил проблему еще немного. Оказывается, spring делает некоторое кэширование свойств, принадлежащих различным классам. В орг.springframework.зернышки.BeanWrapperImpl, метод getPropertyValue, существует следующее:

private Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException {
    String propertyName = tokens.canonicalName;
    String actualName = tokens.actualName;
    PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
    if (pd == null || pd.getReadMethod() == null) {
        throw new NotReadablePropertyException(getRootClass(), this.nestedPath + propertyName);
    }

проблема в том, что cachedIntrospectionResults не содержит рассматриваемого свойства, но содержит все остальные свойства класса. Нужно будет покопать еще немного, чтобы попытаться выяснить, почему он отсутствует, если он отсутствует с самого начала или если он потеряется где-то вдоль линии.

кроме того, я заметил, что отсутствующие свойства-это те, у которых нет сеттеров, только геттеры. И, похоже, контекст осведомлен, как указано в stacktrace. Поэтому не найти свойство при посещении одной страницы не означает, что его нет при посещении другой.

обновление 9:

еще один день, еще отладка. Вообще-то, я нашел кое-что хорошее. Вызов getCachedIntrospectionResults () в предыдущем блоке кода вызвал вызов cachedintrospectionresults#forClass (theClassInQuestion). Это возвращает объект CachedIntrospectionResults, содержащий далеко не все ожидаемые свойства (11 из 21). Войдя в метод forClass, я обнаружил:

static CachedIntrospectionResults forClass(Class beanClass) throws BeansException {
    CachedIntrospectionResults results;
    Object value = classCache.get(beanClass);
    if (value instanceof Reference) {
        Reference ref = (Reference) value;
        results = (CachedIntrospectionResults) ref.get();
    }
    else {
        results = (CachedIntrospectionResults) value;
    }
    if (results == null) {
    //build the CachedIntrospectionResults, store it in classCache and return it.

оказалось, что возвращенные CachedIntrospectionResults были найдены classCache.get (beanClass). Так что было сохранено в classCache был поврежден/не содержат все, что надо. Я поставил точку останова на classCache.get (beanClass)-line и попытался запустить это через отладчик:

classCache.put (beanClass, null);

позволяя методу закончить и перестроить CachedIntrospectionResults, вещи начали работать снова. Таким образом, то, что хранится в classCache, не синхронизировано с тем, что было бы и должно быть создано, если бы ему было разрешено перестроить его. Связано ли это с чем-то неправильным в первый раз, когда он построен, или если classCache поврежден где-то вдоль линии, я в настоящее время не знаю.

Я начинаю подозревать, что это имеет какое-то отношение к загрузчикам классов, поскольку я ранее испытывал проблемы из-за изменений в том, как работает загрузчик классов при обновлении Glassfish..

4 ответов


может быть несколько возможных причин. Я не уверен в фактическом, но я могу дать вам способ узнать проблему

Шаг 1: on сервер 2 машина развернуть приложение на Glassfish 3.0.1 build 22, теперь, если он отлично работает на машине сервера 2, это означает, что может возникнуть проблема с библиотеками стеклянных рыб, следующая может быть причиной этой проблемы

  1. любой библиотека, которая отсутствует в Glassfish 3.1 построить 43 что находится в Glassfish 3.0.1 build 22. вы можете решить, скопировав все библиотеки с рабочего сервера Glassfish на новый сервер.
  2. мои библиотеки Glassfish конфликтуют с весенней версией. [Similliar вроде проблемы, с которой я столкнулся на tomcat, и когда я заменил свои весенние libraires от 3.0.1 до 3.0.3, это сработало для меня] , чтобы заменить пружину библиотеки с последним.

Шаг 2: и если результатом шага 1 является то, что приложение не работает на сервере 2 machin на Glassfish 3.0.1 build 22, может быть следующая причина

  1. Если какие-либо библиотеки, которые вы вставили на java lib либо не входит в эту серверную машину, либо имеет разные версии.

  2. любые папки, которые находятся на classpath или с помощью любого переменные среды на сервере 1 либо не существует на сервере 2, либо нет банок или банок с разными версиями


У меня есть коллега, который исследует ошибку, и он смог воссоздать ее в модульном тесте. Это было сделано путем вызова метода, который строит CachedIntroSpectionResults для класса, в то же время подчеркивая jvm путем добавления строк в память с очень низкими настройками памяти. Этот подход не 20/30000 раз.

Что касается причины этого, я получил только устное объяснение, поэтому у меня нет всех деталей, но это было что-то вроде этого: Java имеет свой собственный самоанализ-результаты, и они обернуты весной. Проблема в том, что java-результаты используют мягкие ссылки, которые делают их склонными к сборкам мусора. Таким образом, когда Spring строила свои обертки вокруг этих мягких ссылок в то же самое время, что и сборщик мусора, он фактически очистил часть основы того, что использовала Spring, что привело к "потере"свойств.

решение, похоже, обновляется с весны 3.1.0.Релиз весной 3.1.3.ОСВОБОЖДАТЬ. Здесь, есть некоторые изменения, и Spring больше не обертывает мягкие ссылки при определении свойств класса (мягкие ссылки используются в редких, особых случаях, а не все время). После обновления версии Spring ошибка не была воспроизведена через модульный тест, остается посмотреть, так ли это на практике.

Update: прошло несколько недель, Никаких признаков ошибки. Так что обновление весенней версии работало:)


Я думаю, что я действительно нашел кандидата для причины этого.

после получения ошибки на одном из тестовых серверов после очень короткой продолжительности и небольшого использования мы сделали некоторые дополнительные проверки причины. Оказалось, что тест-сервер имеет только половину доступной памяти, что заставило нас взглянуть на него более внимательно. Оказалось, что он не использовал всю свою память, но при использовании JConsole для исследования использования памяти другой части пространство нового поколения на куче, оказалось, что одно из surivior пространств было заполнено. Я предполагаю, что это сделало части его переполненными, что привело к переполненным частям, которые были GC-ed или недоступны, не будучи там, где это должно было быть.

нам еще предстоит проверить, что это на самом деле проблема в производственной среде, но как только ошибка появится снова, мы проверим, и если это так, мы изменим некоторые настройки памяти, чтобы обеспечить больше места для выживания области кучи нового поколения. (- XX:SurvivorRatio=6 или что-то в этом роде).

таким образом, кажется, что большие приложения Spring MVC нуждаются в большом пространстве survivor, особенно в новых версиях Glassfish.


действительно, была проблема с новым введенным ExtendedBeanInfo класс весной 3.1.0, который был исправлен весной 3.1.1-см. (https://jira.spring.io/browse/SPR-8347).