Problems refreshing a form-on-a-page [message #1003535] |
Mon, 21 January 2013 13:12 |
Urs Beeli Messages: 573 Registered: October 2012 Location: Bern, Switzerland |
Senior Member |
|
|
In my scout application I have a (read-only) form that is being displayed on an AbstractPage as a view.
In my FormPage class I have the following code:
public class TabbedFormPage extends AbstractPage {
private TabbedForm form = null;
@Override
protected void execPageActivated() throws ProcessingException {
form = new TabbedForm();
form.setDisplayHint(IForm.DISPLAY_HINT_VIEW);
form.setDisplayViewId(IForm.VIEW_ID_PAGE_DETAIL);
form.setEnabledGranted(false);
form.startDisplay();
}
@Override
protected void execPageDeactivated() throws ProcessingException {
if (form != null) {
form.doClose();
form = null;
}
}
}
In my TabbedForm class I have the following code (excerpt only):
public void startDisplay() throws ProcessingException {
startInternal(new DisplayHandler());
}
public class DisplayHandler extends AbstractFormHandler {
@Override
public void execLoad() throws ProcessingException {
ITabbedProcessService service = SERVICES.getService(ITabbedProcessService.class);
TabbedFormData formData = new TabbedFormData();
exportFormData(formData);
formData = service.load(formData);
importFormData(formData);
setEnabledPermission(new ReadTabbedPermission());
}
}
This works, retrieving my data from the server and displaying it in the form instead of a table.
Now, there are circumstances in which the data in the DB can change (e.g. through modification by other users) and I want the data in the table to be updated. With a table page, I can press F5 to do this, so I wanted to mimick this behaviour. In order to do this, I made the following changes:
In TabbedFormPage I added
@Override
protected void execPageDataLoaded() throws ProcessingException {
if (form != null) {
form.refreshForm();
}
}
And in TabbedForm I refactored the code that retrieves and displays the data out of the DisplayHandler into refreshForm:
public void refreshForm() throws ProcessingException {
ITabbedProcessService service = SERVICES.getService(ITabbedProcessService.class);
TabbedFormData formData = new TabbedFormData();
exportFormData(formData);
formData = service.load(formData);
importFormData(formData);
setEnabledPermission(new ReadTabbedPermission());
}
public class DisplayHandler extends AbstractFormHandler {
@Override
public void execLoad() throws ProcessingException {
refreshForm();
}
}
This works well when displaying the form initially, the data is retrieved from the server and displayed in the form.
However, when I press F5 and the line
formData = service.load(formData)
is called, the server throws the following exception:
!ENTRY org.eclipse.scout.rt.server 4 0 2013-01-21 14:00:21.317
!MESSAGE org.eclipse.scout.rt.server.ServiceTunnelServlet.doPost(ServiceTunnelServlet.java:322) Session=8emgq3ix80g2lntr7bgx6vn5, Client=u208490@127.0.0.1/127.0.0.1
!STACK 0
java.lang.SecurityException: value is of inconsistent type; potential value corruption attack
at org.eclipse.scout.rt.shared.data.form.fields.AbstractValueFieldData.readObject(AbstractValueFieldData.java:59)
at sun.reflect.GeneratedMethodAccessor11.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
at java.lang.reflect.Method.invoke(Method.java:611)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1044)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1866)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1770)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1346)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:365)
at java.util.HashMap.readObject(HashMap.java:875)
at sun.reflect.GeneratedMethodAccessor10.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
at java.lang.reflect.Method.invoke(Method.java:611)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1044)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1866)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1770)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1346)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1964)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1888)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1770)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1346)
at java.io.ObjectInputStream.readArray(ObjectInputStream.java:1684)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1340)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1964)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1888)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1770)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1346)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:365)
at org.eclipse.scout.rt.shared.servicetunnel.DefaultServiceTunnelContentHandler.getData(DefaultServiceTunnelContentHandler.java:391)
at org.eclipse.scout.rt.shared.servicetunnel.DefaultServiceTunnelContentHandler.read(DefaultServiceTunnelContentHandler.java:373)
at org.eclipse.scout.rt.shared.servicetunnel.DefaultServiceTunnelContentHandler.readRequest(DefaultServiceTunnelContentHandler.java:319)
at org.eclipse.scout.rt.server.ServiceTunnelServlet.deserializeInput(ServiceTunnelServlet.java:328)
at org.eclipse.scout.rt.server.ServiceTunnelServlet.doPost(ServiceTunnelServlet.java:280)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:755)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
at org.eclipse.scout.http.servletfilter.HttpServletEx.access$0(HttpServletEx.java:1)
at org.eclipse.scout.http.servletfilter.HttpServletEx$1.service(HttpServletEx.java:38)
at org.eclipse.scout.http.servletfilter.internal.FilterChainImpl.doFilter(FilterChainImpl.java:44)
at org.eclipse.scout.http.servletfilter.helper.HttpAuthJaasFilter.doFilter(HttpAuthJaasFilter.java:62)
at org.eclipse.scout.http.servletfilter.internal.FilterChainImpl.doFilter(FilterChainImpl.java:41)
at org.eclipse.scout.http.servletfilter.helper.DevelopmentAuthFilter.doFilter(DevelopmentAuthFilter.java:61)
at org.eclipse.scout.http.servletfilter.internal.FilterChainImpl.doFilter(FilterChainImpl.java:41)
at org.eclipse.scout.http.servletfilter.security.AbstractChainableSecurityFilter.doFilter(AbstractChainableSecurityFilter.java:103)
at org.eclipse.scout.http.servletfilter.internal.FilterChainImpl.doFilter(FilterChainImpl.java:41)
at org.eclipse.scout.http.servletfilter.security.AbstractChainableSecurityFilter.doFilterInternal(AbstractChainableSecurityFilter.java:220)
at org.eclipse.scout.http.servletfilter.security.AbstractChainableSecurityFilter.access$0(AbstractChainableSecurityFilter.java:219)
at org.eclipse.scout.http.servletfilter.security.AbstractChainableSecurityFilter$1.run(AbstractChainableSecurityFilter.java:157)
at java.security.AccessController.doPrivileged(AccessController.java:284)
at javax.security.auth.Subject.doAs(Subject.java:573)
at org.eclipse.scout.http.servletfilter.security.AbstractChainableSecurityFilter.doFilter(AbstractChainableSecurityFilter.java:149)
at org.eclipse.scout.http.servletfilter.internal.FilterChainImpl.doFilter(FilterChainImpl.java:41)
at org.eclipse.scout.http.servletfilter.security.AbstractChainableSecurityFilter.doFilter(AbstractChainableSecurityFilter.java:103)
at org.eclipse.scout.http.servletfilter.internal.FilterChainImpl.doFilter(FilterChainImpl.java:41)
at org.eclipse.scout.http.servletfilter.security.AbstractChainableSecurityFilter.doFilter(AbstractChainableSecurityFilter.java:103)
at org.eclipse.scout.http.servletfilter.internal.FilterChainImpl.doFilter(FilterChainImpl.java:41)
at org.eclipse.scout.http.servletfilter.ServletFilterDelegate.delegateServiceMethod(ServletFilterDelegate.java:57)
at org.eclipse.scout.http.servletfilter.HttpServletEx.service(HttpServletEx.java:35)
at org.eclipse.equinox.http.registry.internal.ServletManager$ServletWrapper.service(ServletManager.java:180)
at org.eclipse.equinox.http.servlet.internal.ServletRegistration.service(ServletRegistration.java:61)
at org.eclipse.equinox.http.servlet.internal.ProxyServlet.processAlias(ProxyServlet.java:128)
at org.eclipse.equinox.http.servlet.internal.ProxyServlet.service(ProxyServlet.java:60)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
at org.eclipse.equinox.http.jetty.internal.HttpServerManager$InternalHttpServiceServlet.service(HttpServerManager.java:384)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:598)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:486)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1065)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:413)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:192)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:999)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:111)
at org.eclipse.jetty.server.Server.handle(Server.java:350)
at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:454)
at org.eclipse.jetty.server.AbstractHttpConnection.content(AbstractHttpConnection.java:900)
at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.content(AbstractHttpConnection.java:954)
at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:851)
at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235)
at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:77)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:606)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:46)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:603)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:538)
at java.lang.Thread.run(Thread.java:736)
Obviously I'm doing something I'm not supposed to do , can anyone tell me what exactly it is, that I did wrong or how I can correctly refresh the form data of an already open and populated form (I'm aware that this would discard any changes made since the initial opening, but since it is a read-only form, I don't mind)?
|
|
|
|
|
|
Re: Problems refreshing a form-on-a-page [message #1005746 is a reply to message #1005379] |
Mon, 28 January 2013 14:09 |
Urs Beeli Messages: 573 Registered: October 2012 Location: Bern, Switzerland |
Senior Member |
|
|
Jeremie
I was going to write a quick reply but somehow ended up debugging for the last 3 hours
I've been to track down the problem and I believe it is a scout bug. Maybe you can quickly confirm me analysis before I open a bug, maybe I missed something.
Anyway, I've been able to narrow down the problem to a UTCDateField on my form.
In my client code (the form) I have the following code snippet which is called from DisplayHandler.execLoad() for the inital display (then it works) and also when on the corresponding page the F5 key is used:
public void refreshForm() throws ProcessingException {
ITabbedProcessService service = SERVICES.getService(ITabbedProcessService.class);
TabbedFormData formData = new TabbedFormData();
exportFormData(formData);
formData = service.load(formData); // server exception is thrown when executing this line
importFormData(formData);
setEnabledPermission(new ReadTabbedPermission());
}
I set a breakpoint in my ProcessService.load() method but that never gets called, the exception is thrown earlier. Digging down into the server, I found out that the exception is being thrown in AbstractValueFieldData.readObject
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
//verify if valueSet and the type of the value are valid and consistent
if (!isValueSet()) {
m_value = null;
}
if (m_value == null) {
return;
}
if (!getHolderType().isAssignableFrom(m_value.getClass())) {
throw new SecurityException("value is of inconsistent type; potential value corruption attack");
}
}
This throws an exception for TabbedFormData$UTC (the form data field for an AbstractUTCDateField on the form) on the refresh.
During the inital call, m_value is of type UTCDate and getHolderType() returns UTCDate as well.
During the refresh call, m_value is of type Date but getHolderType() still returns UTCDate. This causes the isAssignableFrom() to fail and causes the exception.
Removing the UTCDateField from the form solves this problem, I can now easily press both F5 to refresh or use the AbstractResetButton on the form.
So, I re-added the UTCDateField again did some debugging on the client side:
I stepped through the code below and after each step, inspected formData.m_fieldMap.elementData[i].value.m_value (where i is the index for the UTCDate field's data member).
ITabbedProcessService service = SERVICES.getService(ITabbedProcessService.class);
TabbedFormData formData = new TabbedFormData();
exportFormData(formData);
formData = service.load(formData);
importFormData(formData);
setEnabledPermission(new ReadTabbedPermission());
The result was as follows:
--- initial load ---
after new: m_value = null
after exportFormData: m_value = null (no data in the form yet)
after service.load: m_value = UTCDate(...)
after importFormData: m_value = UTCDate(...)
--- refresh ---
after new: m_value = null
after exportFormData: m_value = Date() -> there was data in the form, this seems to be the difference between the two cases
So it seems that the problem comes down to exportFormData being handled wrongly for UTCDateField when there is content already in the form.
Digging down a bit deeper, the problem occurs in:
AbstractValueField.exportFormFieldData -> v.setValue(this.getValue());
AbstractValueField.getValue -> return (T) propertySupport.getProperty(PROP_VALUE)
BasicPropertySupport.getProperty -> m_props.get(name)
--> this returns the value property. Looking a m_props.elementData[i].value, this is of type Date().
Where does this property come from?
Setting a breakpoint in AbstractVieldFalue.setValue() shows that it is called with a rawValue of type UTCDate but then on the line
validatedValue = validateValue(rawValue);
this type is converted to Date because AbstractDateField overwrites validateValueInternal() but AbstractUTCDateField does not, so AbstractDateField.validateValueInternal() is called for UTCDateFields as well:
@Override
protected Date validateValueInternal(Date rawValue) throws ProcessingException {
//legacy support
Object legacyValue = rawValue;
if (legacyValue instanceof Number) {
rawValue = DateUtility.convertDoubleTimeToDate((Number) legacyValue);
}
Date validValue = null;
rawValue = super.validateValueInternal(rawValue);
try {
// apply format
DateFormat df = getDateFormat();
rawValue = df.parse(df.format(rawValue)); --> this line converts rawValue from UTCDate to Date!!!
}
catch (Throwable t) {
// nop, take raw value
}
validValue = rawValue;
return validValue;
}
So, I believ that this is a Scout bug and that AbstractUTCDateField needs to have its own validateValueInternal method which returns a UTCDate object instead of the Date object returned by AbstractDateField.validateValueInternal.
Do you concur with this analysis? If so, I will open a bug for this issue.
|
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.04660 seconds