You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@wicket.apache.org by Thorsten Schöning <ts...@am-soft.de> on 2014/11/21 17:22:09 UTC

Bes practice for deployment to avoid ClassNotFoundException

Hi all,

I'm new to wicket and just recently started to use it for a new web
app for one of our projects. Today I came across a similar issue like
described in [1], a ClassNotFoundException during deserialization of a
page. The interesting part about this in my mind is, that the
mentioned missing class was missing for a good reason: I simply
refactored a bit and renamed the class, redeployed and restarted my
Tomcat. This looks like a normal use case to me, but is something I
didn't thought of when I've read through the docs because.

My deployment is very simple: I check out a SVN working copy with a
pre-configured tag containing the whole application once and
afterwards just need to merge/update and stop/start the web
server/app.

How is deployment supposed to work in Wicket after I have refactored
or otherwise changed my classes regarding cached and serialized pages?
Is there anything I need to tell wicket to clear its caches on each
deployment or ignore those or whatever? Am I forced to not delete once
deployed classes for a fair period of time? That would be a litte
nightmare... Or is there any other issue I ran into I should have
avoided?

Obviously others do deploy their Wicket apps as well, so I hope you
have some input on how to avoid the problem I've ran into. Thanks!

The important parts of the stacktrace:

java.lang.ClassNotFoundException: de.am_soft.util.frontend.wicket.markup.link.LogoutLink
     at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1720)
     at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1571)
     at java.lang.Class.forName0(Native Method)
     at java.lang.Class.forName(Unknown Source)
     at org.apache.wicket.application.AbstractClassResolver.resolveClass(AbstractClassResolver.java:108)
     at org.apache.wicket.serialize.java.JavaSerializer$ClassResolverObjectInputStream.resolveClass(JavaSerializer.java:218)
     at java.io.ObjectInputStream.[...]
     [...]
     at org.apache.wicket.serialize.java.JavaSerializer.deserialize(JavaSerializer.java:122)
     at org.apache.wicket.pageStore.DefaultPageStore.deserializePage(DefaultPageStore.java:396)
     at org.apache.wicket.pageStore.DefaultPageStore.getPage(DefaultPageStore.java:135)
     at org.apache.wicket.page.PageStoreManager$SessionEntry.getPage(PageStoreManager.java:203)
     at org.apache.wicket.page.PageStoreManager$PersistentRequestAdapter.getPage(PageStoreManager.java:360)
     at org.apache.wicket.page.AbstractPageManager.getPage(AbstractPageManager.java:107)

[1]: https://issues.apache.org/jira/browse/WICKET-4785

Mit freundlichen Grüßen,

Thorsten Schöning

-- 
Thorsten Schöning       E-Mail: Thorsten.Schoening@AM-SoFT.de
AM-SoFT IT-Systeme      http://www.AM-SoFT.de/

Telefon...........05151-  9468- 55
Fax...............05151-  9468- 88
Mobil..............0178-8 9468- 04

AM-SoFT GmbH IT-Systeme, Brandenburger Str. 7c, 31789 Hameln
AG Hannover HRB 207 694 - Geschäftsführer: Andreas Muchow


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
For additional commands, e-mail: users-help@wicket.apache.org


Re: Bes practice for deployment to avoid ClassNotFoundException

Posted by Thorsten Schöning <ts...@am-soft.de>.
Guten Tag Martin Grigorov,
am Sonntag, 23. November 2014 um 12:20 schrieben Sie:

> 4) you start Tomcat and it tries to load the persisted sessions
> 4.1) Since Wicket stores its data
> as org.apache.wicket.pageStore.DefaultPageStore.SerializedPage (a triple of
> pageId (int) / sessionId (String) / pageData (byte[])) it is Wicket's job
> to deserialize the pageData byte[] to SomePage instance.

I'm pretty sure this is not what is happening and the provided
stacktrace makes clear that Wicket is doing everything. The only thing 
where Tomcat comes into play is the actual class loading itself, but
that's correct of course because Tomcat provides at least a parent
classloader to use. Wicket does serialize and deserialize the pages on
it's own just because of an incoming request.

> In both cases I think Tomcat will just log an error that an old session
> cannot be loaded and continue with the start of the application.
> Am I correct or Tomcat fails to start ?

Tomcat doesn't log anything and just succeeds to start, at least I
didn't find any log messages from Tomcat in Eclipse and requesting
other pages from Wicket after ClassNotFoundException succeeded as
well. Simply because there were newly created.

> Please paste the complete exception if I am not correct.

I attached the saved HTML file with everything Wicket told me, but
there's nothing more interesting than what I've already posted in my
fist mail.

> No.
> Wicket cleans up its data storages at session invalidation time (per
> sessionId) or at application stop (for all sessions).
> So there is nothing to load at start time and no knowledge about old stuff.

If that was true, Tomcat wouldn't be able to run into any problems with
a serialized session as well because there wouldn't be anything left
from Wicket to serialize. Or does Wicket only clear persistent storage
in files, but retains it's session data? That wouldn't make much sense
to me, because the problem would remain.

Besides that, I just tested with a request for some page: Wicket did
create a folder to save the serialized page during runtime of Tomcat.
So I stopped Tomcat to see if the folder gets deleted or such and it
didn't, instead Wicket created the additional file "DiskDataStoreIndex"
as serialized Java object, which clearly contains the path to the
serialized page. Afterwards I restarted Tomcat, issued another
request, "DiskDataStoreIndex" got read (monitored using ProcMon) and
afterwards deleted. The only thing I didn't see was reading the former
persisted page... But the former behavior corresponds to the code:
DiskDataStore.destroy doesn't just empty anything, data gets persisted
in a file and there's even a load method to load the data.

So the only interesting thing is if pages are used as well after the
restart or not: Application.internalDestroy does call a lot of destroy
methods, but DiskDataStore.destroy e.g. is implemented to persist
data, while other caches seem to really be just cleared, like that for
markup.

Which is the codepath where I can see that persisted pages get
forgotten on shutdown of Wicket? Following Application.internalDestroy
and it's call of internalGetPageManager().destroy(); lead me to
DiskDataStore which clearly doesn't destroy the data...

A simple explanation for my problem could as well be that I didn't
restart the Tomcat and that during it's restart it works like you say,
but in that case I want to make sure that really session and
persistent page store are abandoned and currently it doesn't look
so...

Mit freundlichen Grüßen,

Thorsten Schöning

-- 
Thorsten Schöning       E-Mail: Thorsten.Schoening@AM-SoFT.de
AM-SoFT IT-Systeme      http://www.AM-SoFT.de/

Telefon...........05151-  9468- 55
Fax...............05151-  9468- 88
Mobil..............0178-8 9468- 04

AM-SoFT GmbH IT-Systeme, Brandenburger Str. 7c, 31789 Hameln
AG Hannover HRB 207 694 - Geschäftsführer: Andreas Muchow

Re: Bes practice for deployment to avoid ClassNotFoundException

Posted by Martin Grigorov <mg...@apache.org>.
Hallo Thorsten,

On Sun, Nov 23, 2014 at 1:02 PM, Thorsten Schöning <ts...@am-soft.de>
wrote:

> Guten Tag Martin Grigorov,
> am Sonntag, 23. November 2014 um 11:22 schrieben Sie:
>
> > You are experiencing Tomcat Session Persistence -
> >
> http://tomcat.apache.org/tomcat-7.0-doc/config/manager.html#Persistence_Across_Restarts
> > Just disable it for development to avoid such kind of problems.
>
> But the exception clearly comes from wicket's page store, I already
> found the code where the exception is captured and rethrown as
> RuntimeException. I really doubt this is a problem of the Tomcat.
>

Here is what happens:
1) Wicket stores a Page into the HTTP session
2) Tomcat shuts down and persists all HTTP sessions into the disk
3) you deploy and new version of the app (with incompatible changes in the
classes, like renaming a class)
4) you start Tomcat and it tries to load the persisted sessions
4.1) Since Wicket stores its data
as org.apache.wicket.pageStore.DefaultPageStore.SerializedPage (a triple of
pageId (int) / sessionId (String) / pageData (byte[])) it is Wicket's job
to deserialize the pageData byte[] to SomePage instance. That's why you
think Wicket is to blame here. But even if there was a Page stored as an
attribute in the http session then Tomcat would fail the same way since
there is no such class in the webapp class loader anymore.

In both cases I think Tomcat will just log an error that an old session
cannot be loaded and continue with the start of the application.
Am I correct or Tomcat fails to start ?
Please paste the complete exception if I am not correct.


>
> > 122: return ois.readObject();
> > at
> org.apache.wicket.serialize.java.JavaSerializer.deserialize(JavaSerializer.java:122)
> > java.lang.RuntimeException: Could not deserialize object from byte[]
> > throw new RuntimeException("Could not deserialize object from byte[]",
> cnfx);
>
> Additionally, deactivating this feature for development only in Tomcat
> wouldn't change anything, if I update the production system after
> refactoring I would get the same error there of course.
>

No.
Wicket cleans up its data storages at session invalidation time (per
sessionId) or at application stop (for all sessions).
So there is nothing to load at start time and no knowledge about old stuff.


>
> From looking at the code I think what I would need to do instead is
> create my own delegating serializer, which encapsulates the default
> Wicket one, catches the problematic exception and returns null instead
> of throwing the exception. It looks like that all code mentioned in
> the stacktrace is using null if things are not found or such and would
> ultimately lead to PageProvider.isNewPageInstance believing that a new
> instance is needed:
>
> > public boolean isNewPageInstance()
> > {
> >                boolean isNew = pageInstance == null;
> >               if (isNew && pageId != null)
> >               {
> >                               IRequestablePage storedPageInstance =
> getStoredPage(pageId);
> >                               if (storedPageInstance != null)
> >                               {
> >                                               pageInstance =
> storedPageInstance;
> >                                               isNew = false;
> >                               }
> >               }
> >
> >               return isNew;
> > }
>
> storedPageInstance would simply be null when using my own serializer.
> From my opinion this should be a at least configurable default
> behavior in Wicket anyways, I don't see how else it can handle things
> like refactoring with using it's own page store of serialized Java
> classes. It must ignore ClassNotFoundException in such cases somehow
> because it's a perfectly valid situation.
>
> Mit freundlichen Grüßen,
>
> Thorsten Schöning
>
> --
> Thorsten Schöning       E-Mail: Thorsten.Schoening@AM-SoFT.de
> AM-SoFT IT-Systeme      http://www.AM-SoFT.de/
>
> Telefon...........05151-  9468- 55
> Fax...............05151-  9468- 88
> Mobil..............0178-8 9468- 04
>
> AM-SoFT GmbH IT-Systeme, Brandenburger Str. 7c, 31789 Hameln
> AG Hannover HRB 207 694 - Geschäftsführer: Andreas Muchow
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
> For additional commands, e-mail: users-help@wicket.apache.org
>
>

Re: Bes practice for deployment to avoid ClassNotFoundException

Posted by Thorsten Schöning <ts...@am-soft.de>.
Guten Tag Martin Grigorov,
am Sonntag, 23. November 2014 um 11:22 schrieben Sie:

> You are experiencing Tomcat Session Persistence -
> http://tomcat.apache.org/tomcat-7.0-doc/config/manager.html#Persistence_Across_Restarts
> Just disable it for development to avoid such kind of problems.

But the exception clearly comes from wicket's page store, I already
found the code where the exception is captured and rethrown as
RuntimeException. I really doubt this is a problem of the Tomcat.

> 122: return ois.readObject();
> at org.apache.wicket.serialize.java.JavaSerializer.deserialize(JavaSerializer.java:122)
> java.lang.RuntimeException: Could not deserialize object from byte[]
> throw new RuntimeException("Could not deserialize object from byte[]", cnfx);

Additionally, deactivating this feature for development only in Tomcat
wouldn't change anything, if I update the production system after
refactoring I would get the same error there of course.

From looking at the code I think what I would need to do instead is
create my own delegating serializer, which encapsulates the default
Wicket one, catches the problematic exception and returns null instead
of throwing the exception. It looks like that all code mentioned in
the stacktrace is using null if things are not found or such and would
ultimately lead to PageProvider.isNewPageInstance believing that a new
instance is needed:

> public boolean isNewPageInstance()
> {
>                boolean isNew = pageInstance == null;
>               if (isNew && pageId != null)
>               {
>                               IRequestablePage storedPageInstance = getStoredPage(pageId);
>                               if (storedPageInstance != null)
>                               {
>                                               pageInstance = storedPageInstance;
>                                               isNew = false;
>                               }
>               }
> 
>               return isNew;
> }

storedPageInstance would simply be null when using my own serializer.
From my opinion this should be a at least configurable default
behavior in Wicket anyways, I don't see how else it can handle things
like refactoring with using it's own page store of serialized Java
classes. It must ignore ClassNotFoundException in such cases somehow
because it's a perfectly valid situation.

Mit freundlichen Grüßen,

Thorsten Schöning

-- 
Thorsten Schöning       E-Mail: Thorsten.Schoening@AM-SoFT.de
AM-SoFT IT-Systeme      http://www.AM-SoFT.de/

Telefon...........05151-  9468- 55
Fax...............05151-  9468- 88
Mobil..............0178-8 9468- 04

AM-SoFT GmbH IT-Systeme, Brandenburger Str. 7c, 31789 Hameln
AG Hannover HRB 207 694 - Geschäftsführer: Andreas Muchow


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
For additional commands, e-mail: users-help@wicket.apache.org


Re: Bes practice for deployment to avoid ClassNotFoundException

Posted by Martin Grigorov <mg...@apache.org>.
Hi,

You are experiencing Tomcat Session Persistence -
http://tomcat.apache.org/tomcat-7.0-doc/config/manager.html#Persistence_Across_Restarts
Just disable it for development to avoid such kind of problems.

I'd also recommend to use Jetty for development if possible. It is much
faster to restart. See src/test/java/.../Start.java in Wicket Quickstart.

Martin Grigorov
Wicket Training and Consulting
https://twitter.com/mtgrigorov

On Fri, Nov 21, 2014 at 6:22 PM, Thorsten Schöning <ts...@am-soft.de>
wrote:

> Hi all,
>
> I'm new to wicket and just recently started to use it for a new web
> app for one of our projects. Today I came across a similar issue like
> described in [1], a ClassNotFoundException during deserialization of a
> page. The interesting part about this in my mind is, that the
> mentioned missing class was missing for a good reason: I simply
> refactored a bit and renamed the class, redeployed and restarted my
> Tomcat. This looks like a normal use case to me, but is something I
> didn't thought of when I've read through the docs because.
>
> My deployment is very simple: I check out a SVN working copy with a
> pre-configured tag containing the whole application once and
> afterwards just need to merge/update and stop/start the web
> server/app.
>
> How is deployment supposed to work in Wicket after I have refactored
> or otherwise changed my classes regarding cached and serialized pages?
> Is there anything I need to tell wicket to clear its caches on each
> deployment or ignore those or whatever? Am I forced to not delete once
> deployed classes for a fair period of time? That would be a litte
> nightmare... Or is there any other issue I ran into I should have
> avoided?
>
> Obviously others do deploy their Wicket apps as well, so I hope you
> have some input on how to avoid the problem I've ran into. Thanks!
>
> The important parts of the stacktrace:
>
> java.lang.ClassNotFoundException:
> de.am_soft.util.frontend.wicket.markup.link.LogoutLink
>      at
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1720)
>      at
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1571)
>      at java.lang.Class.forName0(Native Method)
>      at java.lang.Class.forName(Unknown Source)
>      at
> org.apache.wicket.application.AbstractClassResolver.resolveClass(AbstractClassResolver.java:108)
>      at
> org.apache.wicket.serialize.java.JavaSerializer$ClassResolverObjectInputStream.resolveClass(JavaSerializer.java:218)
>      at java.io.ObjectInputStream.[...]
>      [...]
>      at
> org.apache.wicket.serialize.java.JavaSerializer.deserialize(JavaSerializer.java:122)
>      at
> org.apache.wicket.pageStore.DefaultPageStore.deserializePage(DefaultPageStore.java:396)
>      at
> org.apache.wicket.pageStore.DefaultPageStore.getPage(DefaultPageStore.java:135)
>      at
> org.apache.wicket.page.PageStoreManager$SessionEntry.getPage(PageStoreManager.java:203)
>      at
> org.apache.wicket.page.PageStoreManager$PersistentRequestAdapter.getPage(PageStoreManager.java:360)
>      at
> org.apache.wicket.page.AbstractPageManager.getPage(AbstractPageManager.java:107)
>
> [1]: https://issues.apache.org/jira/browse/WICKET-4785
>
> Mit freundlichen Grüßen,
>
> Thorsten Schöning
>
> --
> Thorsten Schöning       E-Mail: Thorsten.Schoening@AM-SoFT.de
> AM-SoFT IT-Systeme      http://www.AM-SoFT.de/
>
> Telefon...........05151-  9468- 55
> Fax...............05151-  9468- 88
> Mobil..............0178-8 9468- 04
>
> AM-SoFT GmbH IT-Systeme, Brandenburger Str. 7c, 31789 Hameln
> AG Hannover HRB 207 694 - Geschäftsführer: Andreas Muchow
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
> For additional commands, e-mail: users-help@wicket.apache.org
>
>