Skip to content

Commit 2be4cd2

Browse files
HHH-15846 Option to add @SuppressFBWarnings annotation on generated code
1 parent ed26c3f commit 2be4cd2

File tree

19 files changed

+356
-34
lines changed

19 files changed

+356
-34
lines changed

hibernate-core/hibernate-core.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ dependencies {
6161
transitive = true
6262
}
6363
testImplementation "joda-time:joda-time:2.3"
64+
testImplementation testLibs.archunitJUnit5
65+
testImplementation testLibs.archunitJUnit4
6466

6567
testRuntimeOnly libs.byteBuddy
6668
testRuntimeOnly testLibs.weld

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/ByteBuddyEnhancementContext.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ public boolean hasLazyLoadableAttributes(TypeDescription classDescriptor) {
6363
return enhancementContext.hasLazyLoadableAttributes( new UnloadedTypeDescription( classDescriptor ) );
6464
}
6565

66+
public boolean addSuppressFBWarnings(TypeDescription classDescriptor) {
67+
return enhancementContext.addSuppressFBWarnings( new UnloadedTypeDescription( classDescriptor ) );
68+
}
69+
6670
public boolean isPersistentField(AnnotatedFieldDescription field) {
6771
return enhancementContext.isPersistentField( field );
6872
}

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/CodeTemplates.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,23 @@ static Collection<Object> getter(Object self) {
543543

544544
}
545545

546+
/**
547+
* Used to suppress <a href="https://spotbugs.readthedocs.io">SpotBugs</a> warnings.
548+
*/
549+
@Retention(RetentionPolicy.CLASS)
550+
@interface SuppressFBWarnings {
551+
/**
552+
* @see "https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html"
553+
*/
554+
String[] value();
555+
556+
/**
557+
* Reason why the warning is suppressed. Use a SpotBugs issue id where appropriate.
558+
*/
559+
String justification();
560+
}
561+
562+
546563
// mapping to get private field from superclass by calling the enhanced reader, for use when field is not visible
547564
static class GetterMapping implements Advice.OffsetMapping {
548565

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/EnhancerImpl.java

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ public class EnhancerImpl implements Enhancer {
7171

7272
private static final CoreMessageLogger log = CoreLogging.messageLogger( Enhancer.class );
7373
private static final Annotation HIBERNATE_VERSION_ANNOTATION;
74+
private static final Annotation SUPPRESS_FB_WARNINGS_ANNOTATION;
7475

7576
static {
7677
HIBERNATE_VERSION_ANNOTATION = new EnhancementInfo() {
@@ -84,6 +85,23 @@ public Class<? extends Annotation> annotationType() {
8485
return EnhancementInfo.class;
8586
}
8687
};
88+
89+
SUPPRESS_FB_WARNINGS_ANNOTATION = new CodeTemplates.SuppressFBWarnings() {
90+
@Override
91+
public String[] value() {
92+
return new String[0];
93+
}
94+
95+
@Override
96+
public String justification() {
97+
return "generated code";
98+
}
99+
100+
@Override
101+
public Class<? extends Annotation> annotationType() {
102+
return CodeTemplates.SuppressFBWarnings.class;
103+
}
104+
};
87105
}
88106

89107
private static final AnnotationDescription TRANSIENT_ANNOTATION = AnnotationDescription.Builder.ofType( Transient.class ).build();
@@ -162,6 +180,16 @@ private TypePool buildTypePool(final ClassFileLocator classFileLocator) {
162180
return TypePool.Default.WithLazyResolution.of( classFileLocator );
163181
}
164182

183+
private List<Annotation> buildOptInAnnotationList(TypeDescription managedCtClass) {
184+
List<Annotation> optInAnnotations = new ArrayList<>();
185+
186+
if ( enhancementContext.addSuppressFBWarnings( managedCtClass ) ) {
187+
optInAnnotations.add(SUPPRESS_FB_WARNINGS_ANNOTATION);
188+
}
189+
190+
return optInAnnotations;
191+
}
192+
165193
private DynamicType.Builder<?> doEnhance(DynamicType.Builder<?> builder, TypeDescription managedCtClass) {
166194
// can't effectively enhance interfaces
167195
if ( managedCtClass.isInterface() ) {
@@ -179,37 +207,43 @@ private DynamicType.Builder<?> doEnhance(DynamicType.Builder<?> builder, TypeDes
179207
return null;
180208
}
181209

210+
List<Annotation> optInAnnotations = buildOptInAnnotationList( managedCtClass );
211+
182212
builder = builder.annotateType( HIBERNATE_VERSION_ANNOTATION );
183213

184214
if ( enhancementContext.isEntityClass( managedCtClass ) ) {
185215
log.debugf( "Enhancing [%s] as Entity", managedCtClass.getName() );
186216
builder = builder.implement( ManagedEntity.class )
187217
.defineMethod( EnhancerConstants.ENTITY_INSTANCE_GETTER_NAME, Object.class, Visibility.PUBLIC )
188-
.intercept( FixedValue.self() );
218+
.intercept( FixedValue.self() )
219+
.annotateMethod( optInAnnotations );
189220

190221
builder = addFieldWithGetterAndSetter(
191222
builder,
192223
EntityEntry.class,
224+
optInAnnotations,
193225
EnhancerConstants.ENTITY_ENTRY_FIELD_NAME,
194226
EnhancerConstants.ENTITY_ENTRY_GETTER_NAME,
195227
EnhancerConstants.ENTITY_ENTRY_SETTER_NAME
196228
);
197229
builder = addFieldWithGetterAndSetter(
198230
builder,
199231
ManagedEntity.class,
232+
optInAnnotations,
200233
EnhancerConstants.PREVIOUS_FIELD_NAME,
201234
EnhancerConstants.PREVIOUS_GETTER_NAME,
202235
EnhancerConstants.PREVIOUS_SETTER_NAME
203236
);
204237
builder = addFieldWithGetterAndSetter(
205238
builder,
206239
ManagedEntity.class,
240+
optInAnnotations,
207241
EnhancerConstants.NEXT_FIELD_NAME,
208242
EnhancerConstants.NEXT_GETTER_NAME,
209243
EnhancerConstants.NEXT_SETTER_NAME
210244
);
211245

212-
builder = addInterceptorHandling( builder, managedCtClass );
246+
builder = addInterceptorHandling( builder, managedCtClass, optInAnnotations );
213247

214248
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
215249
List<AnnotatedFieldDescription> collectionFields = collectCollectionFields( managedCtClass );
@@ -218,42 +252,57 @@ private DynamicType.Builder<?> doEnhance(DynamicType.Builder<?> builder, TypeDes
218252
builder = builder.implement( SelfDirtinessTracker.class )
219253
.defineField( EnhancerConstants.TRACKER_FIELD_NAME, DirtyTracker.class, FieldPersistence.TRANSIENT, Visibility.PRIVATE )
220254
.annotateField( TRANSIENT_ANNOTATION )
255+
.annotateField( optInAnnotations )
221256
.defineMethod( EnhancerConstants.TRACKER_CHANGER_NAME, void.class, Visibility.PUBLIC )
222257
.withParameters( String.class )
223258
.intercept( implementationTrackChange )
259+
.annotateMethod( optInAnnotations )
224260
.defineMethod( EnhancerConstants.TRACKER_GET_NAME, String[].class, Visibility.PUBLIC )
225261
.intercept( implementationGetDirtyAttributesWithoutCollections )
262+
.annotateMethod( optInAnnotations )
226263
.defineMethod( EnhancerConstants.TRACKER_HAS_CHANGED_NAME, boolean.class, Visibility.PUBLIC )
227264
.intercept( implementationAreFieldsDirtyWithoutCollections )
265+
.annotateMethod( optInAnnotations )
228266
.defineMethod( EnhancerConstants.TRACKER_CLEAR_NAME, void.class, Visibility.PUBLIC )
229267
.intercept( implementationClearDirtyAttributesWithoutCollections )
268+
.annotateMethod( optInAnnotations )
230269
.defineMethod( EnhancerConstants.TRACKER_SUSPEND_NAME, void.class, Visibility.PUBLIC )
231270
.withParameters( boolean.class )
232271
.intercept( implementationSuspendDirtyTracking )
272+
.annotateMethod( optInAnnotations )
233273
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_GET_NAME, CollectionTracker.class, Visibility.PUBLIC )
234-
.intercept( implementationGetCollectionTrackerWithoutCollections );
274+
.intercept( implementationGetCollectionTrackerWithoutCollections )
275+
.annotateMethod( optInAnnotations );
235276
}
236277
else {
237278
//TODO es.enableInterfaceExtendedSelfDirtinessTracker ? Careful with consequences..
238279
builder = builder.implement( ExtendedSelfDirtinessTracker.class )
239280
.defineField( EnhancerConstants.TRACKER_FIELD_NAME, DirtyTracker.class, FieldPersistence.TRANSIENT, Visibility.PRIVATE )
240281
.annotateField( TRANSIENT_ANNOTATION )
282+
.annotateField( optInAnnotations )
241283
.defineField( EnhancerConstants.TRACKER_COLLECTION_NAME, CollectionTracker.class, FieldPersistence.TRANSIENT, Visibility.PRIVATE )
242284
.annotateField( TRANSIENT_ANNOTATION )
285+
.annotateField( optInAnnotations )
243286
.defineMethod( EnhancerConstants.TRACKER_CHANGER_NAME, void.class, Visibility.PUBLIC )
244287
.withParameters( String.class )
245288
.intercept( implementationTrackChange )
289+
.annotateMethod( optInAnnotations )
246290
.defineMethod( EnhancerConstants.TRACKER_GET_NAME, String[].class, Visibility.PUBLIC )
247291
.intercept( implementationGetDirtyAttributes )
292+
.annotateMethod( optInAnnotations )
248293
.defineMethod( EnhancerConstants.TRACKER_HAS_CHANGED_NAME, boolean.class, Visibility.PUBLIC )
249294
.intercept( implementationAreFieldsDirty )
295+
.annotateMethod( optInAnnotations )
250296
.defineMethod( EnhancerConstants.TRACKER_CLEAR_NAME, void.class, Visibility.PUBLIC )
251297
.intercept( implementationClearDirtyAttributes )
298+
.annotateMethod( optInAnnotations )
252299
.defineMethod( EnhancerConstants.TRACKER_SUSPEND_NAME, void.class, Visibility.PUBLIC )
253300
.withParameters( boolean.class )
254301
.intercept( implementationSuspendDirtyTracking )
302+
.annotateMethod( optInAnnotations )
255303
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_GET_NAME, CollectionTracker.class, Visibility.PUBLIC )
256-
.intercept( FieldAccessor.ofField( EnhancerConstants.TRACKER_COLLECTION_NAME ) );
304+
.intercept( FieldAccessor.ofField( EnhancerConstants.TRACKER_COLLECTION_NAME ) )
305+
.annotateMethod( optInAnnotations );
257306

258307
Implementation isDirty = StubMethod.INSTANCE, getDirtyNames = StubMethod.INSTANCE, clearDirtyNames = StubMethod.INSTANCE;
259308
for ( AnnotatedFieldDescription collectionField : collectionFields ) {
@@ -319,23 +368,26 @@ private DynamicType.Builder<?> doEnhance(DynamicType.Builder<?> builder, TypeDes
319368
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_CHANGED_FIELD_NAME, void.class, Visibility.PUBLIC )
320369
.withParameters( DirtyTracker.class )
321370
.intercept( getDirtyNames )
371+
.annotateMethod( optInAnnotations )
322372
.defineMethod( EnhancerConstants.TRACKER_COLLECTION_CLEAR_NAME, void.class, Visibility.PUBLIC )
323373
.intercept( Advice.withCustomMapping()
324374
.to( CodeTemplates.ClearDirtyCollectionNames.class, adviceLocator )
325375
.wrap( StubMethod.INSTANCE ) )
376+
.annotateMethod( optInAnnotations )
326377
.defineMethod( ExtendedSelfDirtinessTracker.REMOVE_DIRTY_FIELDS_NAME, void.class, Visibility.PUBLIC )
327378
.withParameters( LazyAttributeLoadingInterceptor.class )
328-
.intercept( clearDirtyNames );
379+
.intercept( clearDirtyNames )
380+
.annotateMethod( optInAnnotations );
329381
}
330382
}
331383

332-
return createTransformer( managedCtClass ).applyTo( builder );
384+
return createTransformer( managedCtClass, optInAnnotations ).applyTo( builder );
333385
}
334386
else if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
335387
log.debugf( "Enhancing [%s] as Composite", managedCtClass.getName() );
336388

337389
builder = builder.implement( ManagedComposite.class );
338-
builder = addInterceptorHandling( builder, managedCtClass );
390+
builder = addInterceptorHandling( builder, managedCtClass, optInAnnotations);
339391

340392
if ( enhancementContext.doDirtyCheckingInline( managedCtClass ) ) {
341393
builder = builder.implement( CompositeTracker.class )
@@ -346,42 +398,45 @@ else if ( enhancementContext.isCompositeClass( managedCtClass ) ) {
346398
Visibility.PRIVATE
347399
)
348400
.annotateField( TRANSIENT_ANNOTATION )
401+
.annotateField( optInAnnotations )
349402
.defineMethod(
350403
EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER,
351404
void.class,
352405
Visibility.PUBLIC
353406
)
354407
.withParameters( String.class, CompositeOwner.class )
355408
.intercept( implementationSetOwner )
409+
.annotateMethod( optInAnnotations )
356410
.defineMethod(
357411
EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER,
358412
void.class,
359413
Visibility.PUBLIC
360414
)
361415
.withParameters( String.class )
362-
.intercept( implementationClearOwner );
416+
.intercept( implementationClearOwner )
417+
.annotateMethod( optInAnnotations );
363418
}
364419

365-
return createTransformer( managedCtClass ).applyTo( builder );
420+
return createTransformer( managedCtClass, optInAnnotations).applyTo( builder );
366421
}
367422
else if ( enhancementContext.isMappedSuperclassClass( managedCtClass ) ) {
368423
log.debugf( "Enhancing [%s] as MappedSuperclass", managedCtClass.getName() );
369424

370425
builder = builder.implement( ManagedMappedSuperclass.class );
371-
return createTransformer( managedCtClass ).applyTo( builder );
426+
return createTransformer( managedCtClass, optInAnnotations).applyTo( builder );
372427
}
373428
else if ( enhancementContext.doExtendedEnhancement( managedCtClass ) ) {
374429
log.debugf( "Extended enhancement of [%s]", managedCtClass.getName() );
375-
return createTransformer( managedCtClass ).applyExtended( builder );
430+
return createTransformer( managedCtClass, optInAnnotations).applyExtended( builder );
376431
}
377432
else {
378433
log.debugf( "Skipping enhancement of [%s]: not entity or composite", managedCtClass.getName() );
379434
return null;
380435
}
381436
}
382437

383-
private PersistentAttributeTransformer createTransformer(TypeDescription typeDescription) {
384-
return PersistentAttributeTransformer.collectPersistentFields( typeDescription, enhancementContext, typePool );
438+
private PersistentAttributeTransformer createTransformer(TypeDescription typeDescription, List<Annotation> optInAnnotations) {
439+
return PersistentAttributeTransformer.collectPersistentFields( typeDescription, optInAnnotations, enhancementContext, typePool );
385440
}
386441

387442
// See HHH-10977 HHH-11284 HHH-11404 --- check for declaration of Managed interface on the class, not inherited
@@ -394,7 +449,7 @@ private boolean alreadyEnhanced(TypeDescription managedCtClass) {
394449
return false;
395450
}
396451

397-
private DynamicType.Builder<?> addInterceptorHandling(DynamicType.Builder<?> builder, TypeDescription managedCtClass) {
452+
private DynamicType.Builder<?> addInterceptorHandling(DynamicType.Builder<?> builder, TypeDescription managedCtClass, List<Annotation> optInAnnotations) {
398453
// interceptor handling is only needed if class has lazy-loadable attributes
399454
if ( enhancementContext.hasLazyLoadableAttributes( managedCtClass ) ) {
400455
log.debugf( "Weaving in PersistentAttributeInterceptable implementation on [%s]", managedCtClass.getName() );
@@ -404,6 +459,7 @@ private DynamicType.Builder<?> addInterceptorHandling(DynamicType.Builder<?> bui
404459
builder = addFieldWithGetterAndSetter(
405460
builder,
406461
PersistentAttributeInterceptor.class,
462+
optInAnnotations,
407463
EnhancerConstants.INTERCEPTOR_FIELD_NAME,
408464
EnhancerConstants.INTERCEPTOR_GETTER_NAME,
409465
EnhancerConstants.INTERCEPTOR_SETTER_NAME
@@ -414,19 +470,23 @@ private DynamicType.Builder<?> addInterceptorHandling(DynamicType.Builder<?> bui
414470
}
415471

416472
private static DynamicType.Builder<?> addFieldWithGetterAndSetter(
417-
DynamicType.Builder<?> builder,
418-
Class<?> type,
419-
String fieldName,
420-
String getterName,
421-
String setterName) {
473+
DynamicType.Builder<?> builder,
474+
Class<?> type,
475+
List<Annotation> annotations,
476+
String fieldName,
477+
String getterName,
478+
String setterName) {
422479
return builder
423480
.defineField( fieldName, type, Visibility.PRIVATE, FieldPersistence.TRANSIENT )
424481
.annotateField( TRANSIENT_ANNOTATION )
482+
.annotateField( annotations )
425483
.defineMethod( getterName, type, Visibility.PUBLIC )
426484
.intercept( FieldAccessor.ofField( fieldName ) )
485+
.annotateMethod( annotations )
427486
.defineMethod( setterName, void.class, Visibility.PUBLIC )
428487
.withParameters( type )
429-
.intercept( FieldAccessor.ofField( fieldName ) );
488+
.intercept( FieldAccessor.ofField( fieldName ) )
489+
.annotateMethod( annotations );
430490
}
431491

432492
private List<AnnotatedFieldDescription> collectCollectionFields(TypeDescription managedCtClass) {

0 commit comments

Comments
 (0)