EN · DE · RU · FR · ES

#800: ExceptionHelper.java

projectforge-business/src/main/java/org/projectforge/framework/utils/ExceptionHelper.java · 97 строк · 50 кода · 39 комментариев · 8 пустых
Три статических служебных метода для обработки исключений: фильтрованные стектрейсы (сворачивание нерелевантных фреймов и пропуск CGLIB-прокси), стектрейс в строку (стандартный printStackTrace в writer) и рекурсивное разрешение корневой причины. Используются в основном уровнем безопасности/доступа для создания читаемых сообщений об ошибках, показывающих только пути кода ProjectForge, а не внутренности Spring/Hibernate/Tomcat.

Методы

МетодЧто делаетЗачем существует
getFilteredStackTrace(ex, namespace)Строит строку стектрейса, содержащую только фреймы из указанного пространства имён. Последовательные неподходящие фреймы сворачиваются в "at ...". Фреймы, содержащие CGLIB$$, всегда пропускаются — это прокси-классы Hibernate с искажёнными именами, например AddressDO$$EnhancerByCGLIB$$a1b2c3d4.Иначе ошибки нарушения доступа показывали бы 50+ строк внутренностей Spring Security и Tomcat, прежде чем появится единственная релевантная строка ProjectForge. Фильтрованный трейс показывает только значимую для разработчика цепочку вызовов.
printStackTrace(ex)Преобразует полный стектрейс в строку через StringWriter+PrintWriter. Стандартный Java-паттерн.Фреймворки логирования требуют строку, а не PrintStream. Используется, когда исключения нужно сериализовать в JSON или сохранить в колонках базы данных.
getRootCause(ex)Рекурсивно разворачивает getCause() до самого внутреннего исключения. Возвращает исходное исключение, если причина отсутствует.Spring сильно оборачивает исключения (UndeclaredThrowableException, InvocationTargetException, DataAccessException). Настоящая ошибка всегда внизу. Этот метод извлекает её.

Алгоритм фильтрации пространства имён

Метод getFilteredStackTrace использует подход конечного автомата с двумя режимами:

Обычный режим: Вывод фреймов. Когда встречается неподходящий фрейм, выводится "at ..." (сворачивание нерелевантных фреймов) и происходит переключение в режим игнорирования.
Режим игнорирования: Пропуск фреймов молча, пока не появится подходящий фрейм, затем переключение обратно в обычный режим и его вывод.

Это даёт компактный вывод, например: at org.projectforge.access.AccessChecker.check(AccessChecker.java:79) at ... at org.projectforge.task.TaskDao.hasAccess(TaskDao.java:176) — где "at ..." заменяет все фреймы Spring/Tomcat/Hibernate между двумя релевантными строками.

Потребители

Вызывается уровнем контроля доступа (AccessCheckerImpl, AccessException) при формировании сообщений об ошибках для нарушений прав доступа. Также используется ScriptExecutor для создания читаемого вывода ошибок при сбоях Groovy/Kotlin-скриптов — без фильтрованного трейса простой NPE в скрипте выдавал бы стектрейс из 200 строк, dominated by Groovy runtime и внутренностями Spring-прокси.

История Git

КоммитЧто изменилось
868d6abb7Копирайт 2025→2026
63081666fКопирайт 2024→2025
b6092df09Копирайт 2023→2024
ab45d51faКопирайт 2001-2022→2001-2023
5f7ef41b8Копирайт 2021→2022
ceb63e8a1Копирайт 2001-2021
7c79f192Копирайт 2020
73a9755dПроход по очистке кода во всех утилитах. Свёрнуты идентичные блоки catch, ArrayList<Class> заменён на diamond-оператор ArrayList<>, StringBuffer заменён на StringBuilder, Collections.sort заменён на List.sort. Сам код ExceptionHelper использует StringBuilder в getFilteredStackTrace — этот коммит обеспечил согласованность с общепроектной миграцией с синхронизированного StringBuffer на несинхронизированный StringBuilder.
Подводный камень — обнаружение CGLIB$$: Метод ignore() проверяет наличие CGLIB$$ в имени класса. Когда ProjectForge обновится до Hibernate 6.x, прокси будут использовать ByteBuddy вместо CGLIB — имена классов будут содержать $HibernateProxy$ или $ByteBuddy$. Метод ignore() нужно будет обновить для обработки обоих паттернов.