春天,NotReadablePropertyException和Glassfish版本(Spring

2019-09-21 22:44发布

I am working on a web-application that uses Spring MVC.

It has been working fine on Glassfish 3.0.1, but when migrating to Glassfish 3.1, it started acting strange. Some pages are only partially showing, or showing nothing at all, and in the log, a lot of messages of this type:

    [#|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)

The error message isn't incorrect, because the property in question does not have a setter-method (gets its value through the constructor). But like I said, this has not been a problem when using Glassfish 3.0.1, only when using it on the new server with Glassfish 3.1.

Does anyone know if there is something in the Glassfish version that might cause this? Or is it some kind of configuration that is missing on the new server?

Some code:

Controller:

@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();
}

Update:

Restarting Glassfish actually removes the problem - temporarily. I suspect that it might have something to do with the loading of the custom binders, we had some problems with out of memory errors, which I thought had something to do with it, but that has been fixed without fixing this problem.

Update 2:

On the 3.0.1 server, the one of the jvm arguments was -client. On the 3.1-server, it was -server. We changed it to -client, and this made the frequency of the error go down a lot, it was happening every other day with -server, took 2 weeks for it to happen with -client.

Update 3:

Some information about the servers (more can be added if requested..)

Server1 (the working one):

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

Server2 (the one with problems):

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

We are using Spring version 3.1.0.

Update 4:

I recreated the error by renaming a field in a jsp to something that does not exist in the modelattribute.

But, more importantly, I noticed something: The fields where the system can't find the getters are often fields of superclasses of the ones that are referenced in the modelattribute. To continue my example, the SomthingContainer is really like this:

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

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

The reference in the controller stays as is, so it's referencing a field that is in the superclass of the object in question.

Update 5:

I tried connecting to the production server with a debugger after the error occured. I put a breakpoint on the return statement of a controller-method returning the object with the error, and tried to see if I could access the field with problems at the time. And that I could, so the problem must lie within Spring MVC/the generated jsp-classes.

(Also, the field in error was of the type "someobject.something[0].somethingelse[0]", but when the somethingelse-list was empty, there was no error! To me, this implies that it somehow can't find the get-method of a list(?))

Update 6:

It seems that the problem has to do with the generation of Java-classes from the jsps. We have not used precompile jsps when deploying, so they are compiled when first used. The problem occurs the first time a page is visited, and the jsp compiled. I also noticed that once the problem has occured, jsps that are compiled after will all give errors. I've kept a few of the problem generated java files, and upon the next restart I will compare them to the working ones. Getting closer :)

Update 7:

Compared the compiled jsp java files that resulted in an error with ones that did not, and there was no difference. So that kinda leaves that out.

So, I now know that the Java object leaving the controller is fine (checked with debugger), and the java class generated from the jsp is fine. So it must be something in between, now I need to find out what...

Update 8:

Another round of debugging, and narrowed the problem down some more. It turns out that spring does some caching of the properties belonging to the various classes. In org.springframework.beans.BeanWrapperImpl, method getPropertyValue, there is the following:

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);
    }

The problem is that the cachedIntrospectionResults does not contain the property in question, it contains every other property of the class though. Will need to dig some more to try to find out why it is missing, if it's missing from the start or if it gets lost somewhere along the line.

Also, I've noticed that the missing properties are those that do not have setters, only getters. And, it seems to be context aware, as indicated by the stacktrace. So not finding a property when visiting one page does not mean that its not available when visiting another.

Update 9:

Another day, more debugging. Actually found some good stuff. The getCachedIntrospectionResults() call in the previous code block wounded up calling CachedIntrospectionResults#forClass(theClassInQuestion). This returned a CachedIntrospectionResults object, containing far from all of the properties expected (11 of 21). Going into the forClass-method, I found:

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)行断点,并试图通过调试运行以下命令:

classCache.put(beanClass,NULL);

允许完成,并重建CachedIntrospectionResults方法时,事情就开始工作了。 那么,什么是存储在classCache是​​不同步的什么会,如果它被允许重建它,应该创建。 这是否是由于出乱子第一次它是建立,或者如果classCache沿着我不知道现在该行某处损坏。

我开始怀疑这事做与类加载器,因为我以前经历的问题,是由于在更新时Glassfish的类加载器的工作方式的变化..

Answer 1:

可能有不止一个可能的原因。 我不知道实际的,但我可以给你找出问题的办法

第1步: 服务器2台机器上Glassfish上部署3.0.1应用打造22,现在如果它工作正常,服务器2台机器上,这意味着有可能与玻璃鱼库的问题,下面,可能使这个问题

  1. 即在Glassfish3.1版本43是在Glassfish的3.0.1缺少任何库建立22。 您可以通过从工作GlassFish服务器到新服务器上复制所有库解决。
  2. 我要的GlassFish库与Spring版本冲突。 [Similliar那种我所面临在Tomcat上,当我取代了我的春天libraires从3.0.1到3.0.3它为我工作的问题],所以用最新的更换你的春天库。

步骤2:如果步骤1的结果是应用程序未在服务器2上MACHIN Glassfish上3.0.1版本运行22可能有以下原因

  1. 如果任一不包括在此服务器的机器,你都基于Java的lib粘贴任何库或具有不同的版本。

  2. 这是在类路径设置或使用在服务器1中的任何环境变量 ,无论是在服务器2不存在或不具备的罐子或瓶子具有与差异版本的任何文件夹



Answer 2:

我有我的一个同事调查了错误,并且他能够重新创建一个单元测试。 这是通过调用该建立CachedIntroSpectionResults的一类,而在同一时间通过添加字符串内存,具有非常低的内存设置强调了JVM的方法进行。 这种方法使得它失败三万分之二十零倍。

至于它的原因,我只得到口头解释,所以我不知道所有的细节,但它是这样的:Java有它自己的反省,结果,这些都是由Spring包裹。 问题是,在Java的结果利用软引用,这使他们容易产生垃圾收集。 所以,当春天在完全相同的时间垃圾收集器运行建立其围绕这些软引用包装,它实际上清除了一些春用什么样的基础上的,从而导致性能被“丢失”。

该解决方案似乎是从升级春季3.1.0.RELEASE春3.1.3.RELEASE。 在这里,有一些变化,春季不再包确定类的属性时,软引用(软引用罕见,特殊情况下使用,而不是所有的时间)。 春季升级的版本后,该错误一直没有通过单元测试可再现的,但它仍然看,如果这是通过在实践中的情况下使用。

更新:这是一个几个星期,一个没有错误的迹象。 所以更新Spring版本的工作:)



Answer 3:

我想我居然发现了这种情况的原因的候选者。

在很短的时间和很少使用后得到的测试服务器中的一个错误后,我们做的原因一些额外的检查。 原来,在测试服务器刚刚一半的可用内存,这使我们成为它多一点彻底看。 原来,它没有用完所有的内存,但使用JConsole的调查堆上的新一代空间的不同部分的内存使用情况时,原来那surivior空间中的一个被挤得满满的。 我猜,这溢出用它制成的零件,导致溢出份不是在那里它被认为是GC-ED或无法访问。

我们还没有确认这其实是在生产环境中的问题为好,但一旦错误再次轮番上涨,我们会检查,如果是这样的话,我们将改变一些内存设置为允许的生存领域更多的空间新一代堆。 (-XX:SurvivorRatio = 6或类似的东西)。

如此看来,更大的Spring MVC应用程序有一个需要大的生存空间,特别是在GlassFish中的较新版本。



Answer 4:

事实上,曾有过与新引入的一个问题ExtendedBeanInfo在Spring 3.1.0类,它已被固定在Spring 3.1.1 -见( https://jira.spring.io/browse/SPR-8347 )。



文章来源: Spring, NotReadablePropertyException and Glassfish version