I am working on a web application using Spring MVC.
It works great on Glassfish 3.0.1, but when it migrated to Glassfish 3.1 it started acting weird. Some pages are only partially displayed or nothing is displayed at all and there are many messages of this type in the journal:
[#|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 is not erroneous, since this property does not have a setter method (gets its value through the constructor). But, as I said, this was not a problem when using Glassfish 3.0.1, only when using it on a new server with Glassfish 3.1.
Does anyone know if there is anything in the Glassfish version that can cause this? Or is it some kind of configuration missing on the new server?
Some codes:
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; }
Update:
Restarting Glassfish actually fixes the problem - temporarily. I suspect this may have something to do with loading custom binders, we had problems with memory errors that I thought had something to do with it, but this was fixed without fixing this problem.
Update 2:
On server 3.0.1, one of the jvm arguments was -client. On server 3.1, it was a server. We changed it to -client, and this led to a significantly reduced error rate, which happened a day later with the server, and it took 2 weeks for this to happen with -client.
Update 3:
Some server information (more can be added on request ..)
Server1 (working):
Windows Server 2003 Java jdk 6 build 35 Glassfish 3.0.1 build 22 -xmx 1024m
Server2 (one who has 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 the field in jsp to something that does not exist in the model attribute.
But, more importantly, I noticed something: fields in which the system cannot find getters are often areas of superclasses of those referenced in the model attribute. To continue my example, SomthingContainer really looks like this:
public class SuperSomethingContainer { [...] private Something something; public Something getSomething() { return something; } } public class SomethingContainer extends SuperSomethingContainer { [...] }
The link in the controller remains as it is, so it refers to the field that is in the superclass of the object in question.
Update 5:
I tried to connect to the server with a debugger after an error occurred. I set a breakpoint on the return statement of the controller method, which returns an object with an error, and tried to find out if I could access the problem field at that time. And that I could, so the problem should be in Spring MVC / generated jsp classes.
(In addition, the field with the error is of the type "someobject.something [0] .somethingelse [0]", but when some list was empty, there was no error! For me this means that it somehow cannot find get list method (?))
Update 6:
The problem seems to be related to the generation of Java classes from jsps. We did not use precompile jsps during deployment, so they were compiled on first use. The problem occurs when you first visit the page and compile jsp. I also noticed that as soon as the problem arose, jsps that compiled after all will give errors. I saved several problems with the java files being created, and after the next restart I will compare them with the working ones. Nearer:)
Update 7:
Compared compiled jsp java files that led to an error with those that did not, and there was no difference. Therefore, to prevent this from happening.
So now I know that the Java object leaving the controller is fine (checked with a debugger), and the java class generated from jsp is fine. So it should be something in between, now I need to find out that ...
Update 8:
Another round of debugging and narrowed down the problem a bit more. It turns out that Spring does some caching of properties belonging to different classes. In org.springframework.beans.BeanWrapperImpl, the getPropertyValue method has 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 cachedIntrospectionResults does not contain the corresponding property, but contains all the other properties of the class. It will be necessary to dig a little more to try to find out why it is absent, if it is absent from the very beginning or if it is lost somewhere along the line.
In addition, I noticed that the missing properties are those that do not have setters, but only getters. And it seems that this is contextually known, as indicated on the stack. Therefore, not finding a property when visiting one page does not mean that it is not available when visiting another.
Update 9:
Another day, more debugging. Actually found some good stuff. A call to getCachedIntrospectionResults () in the previous code block was wounded by a call to CachedIntrospectionResults # forClass (theClassInQuestion). This returns a CachedIntrospectionResults object that contains far from all expected properties (11 of 21). Going to 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) {
It turned out that the returned CachedIntrospectionResults were found classCache.get (beanClass). So, what was stored in classCache was damaged / did not contain everything that was supposed to. I set a breakpoint on classCache.get (beanClass) -line and tried to run this through the debugger:
classCache.put (beanClass, null);
When you allow the method to shut down and rebuild CachedIntrospectionResults, everything worked again. So, what is stored in classCache is not in sync with what would and should be created if it was allowed to rebuild it. Whether this is due to something wrong when the first time it is built, or if the Cache class is damaged somewhere along the line that I currently do not know.
I'm starting to suspect that this has something to do with class loaders, as I previously experienced problems due to changes in the way the class loader works when updating Glassfish.