11/*
2- * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation
2+ * Copyright (c) 2022, 2025 Contributors to the Eclipse Foundation.
33 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
44 * Copyright 2004 The Apache Software Foundation
55 *
4747import java .util .Collections ;
4848import java .util .Enumeration ;
4949import java .util .List ;
50+ import java .util .Map ;
5051import java .util .Set ;
5152import java .util .concurrent .ConcurrentHashMap ;
5253import java .util .concurrent .ConcurrentLinkedQueue ;
5354import java .util .concurrent .CopyOnWriteArrayList ;
55+ import java .util .concurrent .locks .Lock ;
56+ import java .util .concurrent .locks .ReentrantLock ;
5457import java .util .function .Function ;
5558import java .util .jar .Attributes ;
5659import java .util .jar .Attributes .Name ;
@@ -171,6 +174,9 @@ public final class WebappClassLoader extends GlassfishUrlClassLoader
171174 /** Associated directory context giving access to the resources in this webapp. */
172175 private DirContext jndiResources ;
173176
177+ /** Maps package name to the corresponding lock object. */
178+ private final Map <String , Lock > packageLocks = new ConcurrentHashMap <>();
179+
174180 /**
175181 * Should this class loader delegate to the parent class loader
176182 * <strong>before</strong> searching its own repositories (i.e. the
@@ -1147,6 +1153,8 @@ public void close() throws IOException {
11471153 LOG .log (WARNING , "Parent close method failed." , e );
11481154 }
11491155
1156+ packageLocks .clear ();
1157+
11501158 notFoundResources .clear ();
11511159 resourceEntryCache .clear ();
11521160 pathTimestamps .clear ();
@@ -1244,18 +1252,23 @@ private ResourceEntry findClassInternal(String name) throws ClassNotFoundExcepti
12441252 // Looking up the package
12451253 final int pos = name .lastIndexOf ('.' );
12461254 final String packageName = pos == -1 ? null : name .substring (0 , pos );
1247- final Package pkg ;
1255+ Package pkg ;
12481256 if (packageName == null ) {
12491257 pkg = null ;
12501258 } else {
12511259 pkg = getDefinedPackage (packageName );
12521260
12531261 // Define the package (if null)
12541262 if (pkg == null ) {
1255- if (entry .manifest == null ) {
1256- definePackage (packageName , null , null , null , null , null , null , null );
1257- } else {
1258- definePackage (packageName , entry .manifest , entry .codeBase );
1263+ Lock packageLock = getPackageDefinigLock (packageName );
1264+ packageLock .lock ();
1265+ try {
1266+ pkg = getDefinedPackage (packageName );
1267+ if (pkg == null ) {
1268+ definePackage (packageName , entry );
1269+ }
1270+ } finally {
1271+ packageLock .unlock ();
12591272 }
12601273 }
12611274 }
@@ -1279,6 +1292,30 @@ private ResourceEntry findClassInternal(String name) throws ClassNotFoundExcepti
12791292 }
12801293 }
12811294
1295+ /**
1296+ * Defines a Package of the given name.
1297+ *
1298+ * @param packageName the name of the to-be-defined package
1299+ * @param resourceEntry the resource entry
1300+ */
1301+ private void definePackage (String packageName , ResourceEntry resourceEntry ) {
1302+ if (resourceEntry .manifest == null ) {
1303+ definePackage (packageName , null , null , null , null , null , null , null );
1304+ } else {
1305+ definePackage (packageName , resourceEntry .manifest , resourceEntry .codeBase );
1306+ }
1307+ }
1308+
1309+ /**
1310+ * Returns the lock object for package defining operations.
1311+ *
1312+ * @param packageName the name of the to-be-defined package
1313+ * @return the lock for package defining operations
1314+ */
1315+ private Lock getPackageDefinigLock (String packageName ) {
1316+ return packageLocks .computeIfAbsent (packageName , key -> new ReentrantLock ());
1317+ }
1318+
12821319
12831320 /**
12841321 * Attempts to find the specified resource in local repositories.
0 commit comments