Как избежать ClassCastException в Struts 1.3.x из-за его стратегии загрузки классов?

EDIT: я внес изменения, так как думаю, что в моем вопросе могут отсутствовать некоторые технические детали.

Проблема возникает в Struts 1.3.x и описывается следующим образом:

При изменении классов, связанных со Struts (например, ActionForm, Action и/или любого класса, который я использую с ними), я получаю ClassCastException при тестировании своих изменений (без перезапуска моего веб-приложения).

Если я перезапущу свое веб-приложение после внесения тех же изменений, то исключений не будет, и сделанные изменения будут видны при тестировании.

Такое поведение считается НОРМАЛЬНЫМ по следующим причинам:

Мой веб-контейнер (weblogic) настроен на перезагрузку сервлета и классов при внесении изменений

Я предполагаю, что при изменении моих классов используется другой ClassLoader. Что может вызвать ClassCastException.

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

Что я хочу знать, так это то, что я могу сделать (шаблон программирования? передовой опыт?), чтобы избежать такого рода ClassCastException или избежать перезапуска веб-приложения для просмотра моих изменений?

Вот трассировка стека:

java.lang.ClassCastException: my.package.here.MyActionClassNameHere
    at org.apache.struts.chain.commands.servlet.CreateAction.getAction(CreateAction.java:65)
    at org.apache.struts.chain.commands.AbstractCreateAction.execute(AbstractCreateAction.java:91)
    at org.apache.struts.chain.commands.ActionCommandBase.execute(ActionCommandBase.java:51)
    at org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:191)
    at org.apache.commons.chain.generic.LookupCommand.execute(LookupCommand.java:305)
    at org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:191)
    at org.apache.struts.chain.ComposableRequestProcessor.process(ComposableRequestProcessor.java:283)
    at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1914)
    at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:463)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:821)
    at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
    at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:300)
    at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3650)
    at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
    at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2174)
    at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1446)
    at weblogic.work.ExecuteThread.execute(ExecuteThread.java:201)
    at weblogic.work.ExecuteThread.run(ExecuteThread.java:173)

Я видел страницу о JRebel. Это может быть решением, но я не готов использовать его для своих целей (это какой-то очень простой тест с использованием struts 1.3.x).

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


person user1199680    schedule 20.07.2012    source источник
comment
Я слышал только хорошее о JRebel, но я никогда не использовал его (и это не бесплатно)   -  person JB Nizet    schedule 20.07.2012
comment
@JBNizet : Merci pour ta réponse ^^ (Je ne suis pas Certain si je testerai JRebel juste pour mes petits test...)   -  person user1199680    schedule 23.07.2012
comment
JB Nizet, вы можете использовать его бесплатно, если вы занимаетесь разработкой с открытым исходным кодом, а также Social Plan, который скоро появится =) my.jrebel.com/plans   -  person Aleksandr Motsjonov    schedule 25.07.2012


Ответы (2)


Это риск, на который вы идете, когда в какой-то момент времени у вас есть экземпляр класса, загруженный в два отдельных загрузчика классов. См. http://zeroturnaround.com/blog/reloading-objects-classes-classloaders/ и найдите ClassCastException. В статье очень хорошо объясняется суть загрузки классов в Java.

Что касается решений, помимо JRebel, вы, вероятно, могли бы просто повторно развернуть приложение. Вы сказали, что приложение достаточно маленькое, так что это не должно занять много времени.

person toomasr    schedule 25.07.2012
comment
Да, проблема, с которой я столкнулся, полностью вызвана классами, поступающими из разных загрузчиков классов. Это связано с тем, что я не перезапускаю развертывание после внесения изменений, но основная причина также в том, что Struts 1.3 помещает классы в кэш для оптимизации производительности. Зная, что я думаю, что могу найти решение, чтобы избежать этого исключения. Я надеюсь, что у меня будет свободное время, чтобы написать и объяснить это (большинство вещей, которые нужно знать, находятся в трассировке стека выше). - person user1199680; 28.07.2012
comment
ваша ссылка очень поучительна. Спасибо - person user1199680; 28.07.2012

По умолчанию Struts 1.3 (последняя версия 1.3.x) помещает классы действий в кеш для оптимизации производительности (при перезапуске развертывания кеш очищается). Это было причиной исключения, потому что мои измененные классы загружаются другим загрузчиком классов, отличным от предыдущего, который был в кеше.

Это исключение возникает на этапе извлечения экземпляра действия (который начинается здесь, в трассировке стека).

at org.apache.struts.chain.ComposableRequestProcessor.process(ComposableRequestProcessor.java:283)

По умолчанию эта версия Struts реализует шаблон цепочки ответственности (с использованием файла конфигурации xml chain-config .xml). Таким образом, обработчик запросов, упомянутый выше, делегирует получение экземпляра действия (или создание экземпляра) в CreationAction (которая показана здесь в stactrace):

at org.apache.struts.chain.commands.servlet.CreateAction.getAction(CreateAction.java:65)

Ниже приведены решения, позволяющие избежать ClassCastException при внесении изменений и отказе от перезапуска развертывания.

Решение 1:

  • Расширьте CreateAction с помощью пользовательского класса и переопределите его метод getAction().

  • В getAction(): если super.getAction().getClass() имеет не тот же загрузчик классов, что и новый, заставьте его вернуть новый экземпляр, вызвав super.createAction()< /strong> (Это означает, что я должен знать полное имя моего класса Action при сравнении с помощью оператора ==, иначе мне не следует сравнивать и всегда загружать новый класс действий с помощью createAction)

  • Используйте пользовательский файл chain-config.xml, чтобы указать, что пользовательское CreationAction вместо этого по умолчанию

Решение 2. Расширьте RequestProcessor с помощью пользовательский класс и использовать его вместо класса по умолчанию (т.е. обновить параметры в struts-config.xml)

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

У меня не было свободного времени, чтобы добавить более подробный пример кода, файлы конфигурации и т. д. Я попытаюсь сделать это как-нибудь (где-нибудь еще). Но наверняка я тестирую их, и это сработало для моей цели :)

person user1199680    schedule 28.07.2012