2525import java .io .File ;
2626import java .io .IOException ;
2727import java .io .InputStream ;
28- import java .io .UncheckedIOException ;
2928import java .nio .charset .StandardCharsets ;
3029import java .nio .file .Files ;
3130import java .nio .file .Path ;
3231import java .util .ArrayList ;
33- import java .util .Arrays ;
3432import java .util .HashMap ;
3533import java .util .List ;
3634import java .util .Map ;
37- import java .util .jar .Attributes ;
3835import java .util .jar .JarEntry ;
3936import java .util .jar .JarFile ;
4037import java .util .jar .Manifest ;
4138import java .util .regex .Matcher ;
4239import java .util .regex .Pattern ;
40+ import java .util .zip .ZipEntry ;
4341
42+ /**
43+ * This task generates a file with a class to module mapping
44+ * used to imitate modular behavior during unit tests so
45+ * entitlements can lookup correct policies.
46+ */
4447public abstract class GenerateTestBuildInfoTask extends DefaultTask {
4548
4649 public static final String DESCRIPTION = "generates plugin test dependencies file" ;
47- public static final String PROPERTIES_FILENAME = "test-build-info.json" ;
4850
4951 public GenerateTestBuildInfoTask () {
5052 setDescription (DESCRIPTION );
@@ -64,120 +66,7 @@ public GenerateTestBuildInfoTask() {
6466
6567 @ TaskAction
6668 public void generatePropertiesFile () throws IOException {
67- Map <String , String > classesToModules = new HashMap <>();
68- for (File file : getCodeLocations ().get ().getFiles ()) {
69- if (file .exists ()) {
70- if (file .getName ().endsWith (".jar" )) {
71- try (JarFile jarFile = new JarFile (file )) {
72- String [] moduleName = new String [1 ];
73- JarEntry moduleEntry = jarFile .getJarEntry ("module-info.class" );
74- if (moduleEntry != null ) {
75- try (InputStream miis = jarFile .getInputStream (moduleEntry )) {
76- ClassReader cr = new ClassReader (miis );
77- cr .accept (new ClassVisitor (Opcodes .ASM9 ) {
78- @ Override
79- public ModuleVisitor visitModule (String name , int access , String version ) {
80- // getLogger().lifecycle("FOUND 0: " + name + " | " + file.getAbsolutePath());
81- moduleName [0 ] = name ;
82- return super .visitModule (name , access , version );
83- }
84- }, Opcodes .ASM9 );
85- }
86- } else {
87- moduleEntry = jarFile .getJarEntry ("META-INF/MANIFEST.MF" );
88- if (moduleEntry != null ) {
89- try (InputStream meis = jarFile .getInputStream (moduleEntry )) {
90- Manifest manifest = new Manifest (meis );
91- String mr = manifest .getMainAttributes ().getValue (Attributes .Name .MULTI_RELEASE );
92- if (Boolean .parseBoolean (mr )) {
93- List <Integer > versions = jarFile .stream ()
94- .filter (
95- je -> je .getName ().startsWith ("META-INF/versions/" )
96- && je .getName ().endsWith ("/module-info.class" )
97- )
98- .map (je -> Integer .parseInt (je .getName ().substring (18 , je .getName ().length () - 18 )))
99- .toList ();
100- versions = new ArrayList <>(versions );
101- versions .sort (Integer ::compareTo );
102- versions = versions .reversed ();
103- int major = Runtime .version ().version ().get (0 );
104- StringBuilder path = new StringBuilder ("META-INF/versions/" );
105- for (int version : versions ) {
106- if (version <= major ) {
107- path .append (version );
108- break ;
109- }
110- }
111- if (path .length () > 18 ) {
112- path .append ("/module-info.class" );
113- moduleEntry = jarFile .getJarEntry (path .toString ());
114- if (moduleEntry != null ) {
115- try (InputStream miis = jarFile .getInputStream (moduleEntry )) {
116- ClassReader cr = new ClassReader (miis );
117- cr .accept (new ClassVisitor (Opcodes .ASM9 ) {
118- @ Override
119- public ModuleVisitor visitModule (String name , int access , String version ) {
120- // getLogger().lifecycle("FOUND 1: " + name + " | " + file.getAbsolutePath());
121- moduleName [0 ] = name ;
122- return super .visitModule (name , access , version );
123- }
124- }, Opcodes .ASM9 );
125- }
126- }
127- }
128- }
129- if (moduleName [0 ] == null ) {
130- String amn = manifest .getMainAttributes ().getValue ("Automatic-Module-Name" );
131- if (amn != null ) {
132- // getLogger().lifecycle("FOUND 2: " + amn + " | " + file.getAbsolutePath());
133- moduleName [0 ] = amn ;
134- }
135- }
136- }
137- }
138- if (moduleName [0 ] == null ) {
139- String jn = file .getName ().substring (0 , file .getName ().length () - 4 );
140- Matcher matcher = Pattern .compile ("-(\\ d+(\\ .|$))" ).matcher (jn );
141- if (matcher .find ()) {
142- jn = jn .substring (0 , matcher .start ());
143- }
144- jn = jn .replaceAll ("[^A-Za-z0-9]" , "." );
145- // getLogger().lifecycle("FOUND 3: " + jn + " | " + file.getAbsolutePath());
146- moduleName [0 ] = jn ;
147- }
148- }
149- jarFile .stream ()
150- .filter (
151- je -> je .getName ().startsWith ("META-INF" ) == false
152- && je .getName ().equals ("module-info.class" ) == false
153- && je .getName ().endsWith (".class" )
154- )
155- .findFirst ()
156- .ifPresent (je -> classesToModules .put (je .getName (), moduleName [0 ]));
157- } catch (IOException ioe ) {
158- throw new UncheckedIOException (ioe );
159- }
160- } else if (file .isDirectory ()) {
161- List <File > files = new ArrayList <>(List .of (file ));
162- while (files .isEmpty () == false ) {
163- File find = files .removeFirst ();
164- if (find .exists ()) {
165- if (find .isDirectory () && find .getName ().equals ("META_INF" ) == false ) {
166- files .addAll (Arrays .asList (find .listFiles ()));
167- } else if (find .getName ().equals ("module-info.class" ) == false && find .getName ().contains ("$" ) == false ) {
168- classesToModules .put (
169- find .getAbsolutePath ().substring (file .getAbsolutePath ().length () + 1 ),
170- "module-goes-here"
171- );
172- break ;
173- }
174- }
175- }
176- } else {
177- throw new IllegalArgumentException ("Unrecognized classpath file: " + file );
178- }
179- }
180- }
69+ Map <String , String > classesToModules = buildClassesToModules ();
18170
18271 Path outputDirectory = getOutputDirectory ().get ().getAsFile ().toPath ();
18372 Files .createDirectories (outputDirectory );
@@ -206,4 +95,144 @@ public ModuleVisitor visitModule(String name, int access, String version) {
20695 writer .write ("\n ]\n }\n " );
20796 }
20897 }
98+
99+ private Map <String , String > buildClassesToModules () throws IOException {
100+ Map <String , String > classesToModules = new HashMap <>();
101+ for (File file : getCodeLocations ().get ().getFiles ()) {
102+ if (file .exists ()) {
103+ if (file .getName ().endsWith (".jar" )) {
104+ extractFromJar (file , classesToModules );
105+ } else if (file .isDirectory ()) {
106+ extractFromDirectory (file , classesToModules );
107+ } else {
108+ throw new IllegalArgumentException ("unrecognized classpath entry: " + file );
109+ }
110+ }
111+ }
112+ return classesToModules ;
113+ }
114+
115+ private void extractFromJar (File file , Map <String , String > classesToModules ) throws IOException {
116+ try (JarFile jarFile = new JarFile (file )) {
117+ String className = extractClassNameFromJar (jarFile );
118+ String moduleName = extractModuleNameFromJar (file , jarFile );
119+
120+ if (className != null ) {
121+ classesToModules .put (className , moduleName );
122+ }
123+ }
124+ }
125+
126+ private String extractClassNameFromJar (JarFile jarFile ) {
127+ return jarFile .stream ()
128+ .filter (
129+ je -> je .getName ().startsWith ("META-INF" ) == false
130+ && je .getName ().equals ("module-info.class" ) == false
131+ && je .getName ().endsWith (".class" )
132+ )
133+ .findFirst ().map (ZipEntry ::getName ).orElse (null );
134+ }
135+
136+ private String extractModuleNameFromJar (File file , JarFile jarFile ) throws IOException {
137+ String moduleName = null ;
138+
139+ if (jarFile .isMultiRelease ()) {
140+ List <Integer > versions = jarFile .stream ()
141+ .filter (
142+ je -> je .getName ().startsWith ("META-INF/versions/" )
143+ && je .getName ().endsWith ("/module-info.class" )
144+ )
145+ .map (je -> Integer .parseInt (je .getName ().substring (18 , je .getName ().length () - 18 )))
146+ .toList ();
147+ versions = new ArrayList <>(versions );
148+ versions .sort (Integer ::compareTo );
149+ versions = versions .reversed ();
150+ int major = Runtime .version ().version ().get (0 );
151+ StringBuilder path = new StringBuilder ("META-INF/versions/" );
152+ for (int version : versions ) {
153+ if (version <= major ) {
154+ path .append (version );
155+ break ;
156+ }
157+ }
158+ if (path .length () > 18 ) {
159+ path .append ("/module-info.class" );
160+ JarEntry moduleEntry = jarFile .getJarEntry (path .toString ());
161+ if (moduleEntry != null ) {
162+ try (InputStream inputStream = jarFile .getInputStream (moduleEntry )) {
163+ moduleName = extractModuleNameFromModuleInfo (inputStream );
164+ }
165+ }
166+ }
167+ }
168+
169+ if (moduleName == null ) {
170+ JarEntry moduleEntry = jarFile .getJarEntry ("module-info.class" );
171+ if (moduleEntry != null ) {
172+ try (InputStream inputStream = jarFile .getInputStream (moduleEntry )) {
173+ moduleName = extractModuleNameFromModuleInfo (inputStream );
174+ }
175+ }
176+ }
177+
178+ if (moduleName == null ) {
179+ JarEntry manifestEntry = jarFile .getJarEntry ("META-INF/MANIFEST.MF" );
180+ if (manifestEntry != null ) {
181+ try (InputStream inputStream = jarFile .getInputStream (manifestEntry )) {
182+ Manifest manifest = new Manifest (inputStream );
183+ String amn = manifest .getMainAttributes ().getValue ("Automatic-Module-Name" );
184+ if (amn != null ) {
185+ moduleName = amn ;
186+ }
187+ }
188+ }
189+ }
190+
191+ if (moduleName == null ) {
192+ String jn = file .getName ().substring (0 , file .getName ().length () - 4 );
193+ Matcher matcher = Pattern .compile ("-(\\ d+(\\ .|$))" ).matcher (jn );
194+ if (matcher .find ()) {
195+ jn = jn .substring (0 , matcher .start ());
196+ }
197+ jn = jn .replaceAll ("[^A-Za-z0-9]" , "." );
198+ moduleName = jn ;
199+ }
200+
201+ return moduleName ;
202+ }
203+
204+ private void extractFromDirectory (File file , Map <String , String > classesToModules ) {
205+ String className = extractClassNameFromDirectory (file );
206+ String moduleName = null ;
207+
208+ if (className != null && moduleName != null ) {
209+ classesToModules .put (className , moduleName );
210+ }
211+ }
212+
213+ private String extractClassNameFromDirectory (File file ) {
214+ List <File > files = new ArrayList <>(List .of (file ));
215+ while (files .isEmpty () == false ) {
216+ File find = files .removeFirst ();
217+ if (find .exists ()) {
218+ if (find .getName ().endsWith (".class" ) && find .getName ().equals ("module-info.class" ) == false && find .getName ().contains ("$" ) == false ) {
219+ return find .getAbsolutePath ().substring (file .getAbsolutePath ().length () + 1 );
220+ }
221+ }
222+ }
223+ return null ;
224+ }
225+
226+ private String extractModuleNameFromModuleInfo (InputStream inputStream ) throws IOException {
227+ String [] moduleName = new String [1 ];
228+ ClassReader cr = new ClassReader (inputStream );
229+ cr .accept (new ClassVisitor (Opcodes .ASM9 ) {
230+ @ Override
231+ public ModuleVisitor visitModule (String name , int access , String version ) {
232+ moduleName [0 ] = name ;
233+ return super .visitModule (name , access , version );
234+ }
235+ }, Opcodes .ASM9 );
236+ return moduleName [0 ];
237+ }
209238}
0 commit comments