EN · DE · RU · FR · ES

#1875: BeanHelper.java

projectforge-common/src/main/java/org/projectforge/common/BeanHelper.java Utility class — org.projectforge.common package, projectforge-common/src/main/java/org/projectforge/common/BeanHelper.java 678 lines · 481 code · 150 comments · 47 blank
Comprehensive JavaBean introspection and reflection utility. Core infrastructure for ProjectForge's ORM/Wicket integration layer — provides property name resolution from getter/setter methods, getter/setter method lookup with extensive caching (five internal caches for performance), field annotation discovery, nested/indexed property access, property copying with difference detection, and reflection-based object instantiation. Widely used across all ProjectForge modules for Wicket list sorting, form binding, and DO-to-DTO conversion.

Architecture

Imports

Internal Caching Architecture

BeanHelper uses five static synchronized caches to avoid repeated reflection calls, which are expensive in Java. This is critical for Wicket list pages where BeanHelper is called for every cell in a sortable data table.

CacheKeyValueBenefit
declaredFieldsCacheClassField[]Avoids re-scanning class hierarchy for fields
declaredMethodsCacheClassMethod[]Avoids re-scanning class hierarchy for methods
declaredFieldAnnotationsCache"class:fieldname"Annotation[]Avoids re-resolving annotated fields (critical for Wicket column sorting)
getterMethodsCache"class:fieldname"MethodAvoids re-searching for getter methods
declaredGetterMethodsCacheStringMethod[]Declared but appears unused in provided code — may be populated elsewhere

Key Operations

Property Name Resolution

Annotation Access

getDeclaredAnnotations(Class, fieldname) supports nested property paths (dot-separated, e.g., "address.city.name") by walking the field chain through reflection to find the terminal field's annotations. This is essential for Wicket data tables that display nested object properties with sortable columns.

Property Access (Nested + Indexed)

Property Copying (copyProperties)

Copies specified properties from source to destination object, returning true if any property actually changed. Supports:

Reflection Utilities

Test Mode Feature

The TEST_MODE flag (controlled by enterTestMode()/exitTestMode()) suppresses error logging during test execution. This allows tests to attempt reflection operations that are expected to fail without polluting test logs with stacktraces. The logInstantiationException() methods honor this flag.

Design Decisions

  1. Aggressive caching: Five caches exist because reflection is slow and BeanHelper is called on hot paths (Wicket data table rendering and sorting)
  2. Bridge method filtering: determineGetter() skips bridge methods (synthetic methods created by the compiler for generic type erasure) to return the actual implemented method
  3. Null-safe traversal: getNestedProperty() returns null at any point in the chain where an intermediate value is null, rather than throwing NPE
  4. Constructor accessibility: newInstance() calls constructor.setAccessible(true) to instantiate classes with private constructors
  5. No external bean library: Despite depending on commons-beanutils in the Gradle build, BeanHelper implements its own property resolution rather than using Apache BeanUtils directly — likely for performance control and caching
The isEqualList() helper (package-private) performs element-by-element comparison using iterators, not equals(). This means collection equality is based on element identity/equality of corresponding positions, not set equality — two lists with the same elements in different order are not equal.

Git History

868d6abb7 2025 -> 2026
63081666f Source file headers: 2024-> 2025.
a73905c14 Fix typos in projectforge*/ directories Found via codespell
694987647 Migration stuff in progress... (all tests of all packages: OK).
ae50b2522 BeanHelper.getProperty supports now nested properties by simply using new PropertyUtils.
b6092df09 Copyright 2023 -> 2024
ab45d51fa Copyright 2001-2022 -> 2001-2023.
0203e3261 AbstractListPage: long-running queries: change of sort order shows result list without search button. BeanHelper: lots of caches for faster sort in Wicket result lists.