View Javadoc
1   package org.djutils.reflection;
2   
3   import static org.junit.jupiter.api.Assertions.assertEquals;
4   import static org.junit.jupiter.api.Assertions.assertTrue;
5   
6   import java.io.File;
7   import java.io.IOException;
8   import java.io.InputStream;
9   import java.io.OutputStream;
10  import java.nio.file.Files;
11  import java.nio.file.Path;
12  import java.text.ParseException;
13  import java.text.SimpleDateFormat;
14  import java.util.Date;
15  import java.util.TimeZone;
16  
17  import org.djutils.io.ResourceResolver;
18  import org.djutils.reflection.ClassUtil.ClassFileDescriptor;
19  import org.junit.jupiter.api.Test;
20  
21  /**
22   * ClassFileDescriptorTest tests class file descriptors. <br>
23   * <br>
24   * Copyright (c) 2003-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
25   * for project information <a href="https://www.simulation.tudelft.nl/" target="_blank">www.simulation.tudelft.nl</a>. The
26   * source code and binary code of this software is proprietary information of Delft University of Technology.
27   * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank">Alexander Verbraeck</a>
28   */
29  public class ClassFileDescriptorTest
30  {
31      /**
32       * Test the classFileDescriptor method in ClassUtil.
33       * @throws ParseException on error
34       * @throws IOException on error
35       */
36      @Test
37      public void classFileDescriptorTest() throws ParseException, IOException
38      {
39          // Use a fixed zone for the whole test
40          TimeZone originalTz = TimeZone.getDefault();
41          try
42          {
43              TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
44  
45              var original = ResourceResolver.resolve("/org/djutils-test-resources/test/Test.class");
46              InputStream originalClassStream = original.openStream();
47              File cfdFile = copyToTempFile(originalClassStream, "Test", ".class");
48              var cfd = ResourceResolver.resolve(cfdFile.getAbsolutePath());
49  
50              // change the last accessed date
51              SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
52              formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
53              cfdFile.setLastModified(formatter.parse("2000-01-01 02:03:04").getTime());
54  
55              ClassFileDescriptor cfdClass = ClassUtil.classFileDescriptorForPath(cfd.asPath());
56              assertTrue(cfdClass.getName().startsWith("Test"));
57              assertTrue(cfdClass.getName().endsWith(".class"));
58              Date cfdClassDate = new Date(cfdClass.getLastChangedDate());
59              assertEquals("2000-01-01 02:03:04", formatter.format(cfdClassDate));
60              assertTrue(cfdClass.toString().startsWith("ClassFileDescriptor ["));
61  
62              var originalJar = ResourceResolver.resolve("/org/djutils-test-resources/test/Test.jar");
63              InputStream jarStream = originalJar.openStream();
64              File jarFile = copyToTempFile(jarStream, "Test", ".jar");
65              var jar = ResourceResolver.resolve(jarFile.getAbsolutePath());
66  
67              // change the last accessed date of the jar -- not of the file in the jar
68              jarFile.setLastModified(formatter.parse("2010-11-12 01:02:03").getTime());
69  
70              ClassFileDescriptor cfdJar = ClassUtil.classFileDescriptorForPath(jar.asPath());
71              Date cfdJarDate = new Date(cfdJar.getLastChangedDate());
72              assertTrue(cfdJar.getName().startsWith("Test"));
73              assertTrue(cfdJar.getName().endsWith(".jar"));
74              assertEquals("2010-11-12 01:02:03", formatter.format(cfdJarDate));
75  
76              // find the file in the jar
77              Path cfdJarFilePath = ResourceResolver.resolve(jar.asPath().toString() + "!/Test.class").asPath();
78              ClassFileDescriptor cfdJarFile = ClassUtil.classFileDescriptorForPath(cfdJarFilePath);
79              assertEquals("Test.class", cfdJarFile.getName());
80              Date cfdJarFileDate = new Date(cfdJarFile.getLastChangedDate());
81              assertTrue(formatter.format(cfdJarFileDate).startsWith("2000-01-01"));
82              // Note: time cannot be checked -- file was made in CET, test runs in UTC
83  
84              // if the file in the jar cannot be found, we should get the file descriptor of the jar itself
85              Path cfdJarFilePath2 = ResourceResolver.resolve(jar.asPath().toString() + "!/TestXYZ.class").asPath();
86              ClassFileDescriptor cfdJarFile2 = ClassUtil.classFileDescriptorForPath(cfdJarFilePath2);
87              assertTrue(cfdJarFile2.getName().startsWith("Test"));
88              assertTrue(cfdJarFile2.getName().endsWith(".jar"));
89              Date cfdJarFileDate2 = new Date(cfdJarFile2.getLastChangedDate());
90              assertEquals("2010-11-12 01:02:03", formatter.format(cfdJarFileDate2));
91          }
92          finally
93          {
94              TimeZone.setDefault(originalTz);
95          }
96      }
97  
98      /**
99       * Test the classFileDescriptor method in ClassUtil with a path that contains a space.
100      * @throws ParseException on error
101      * @throws IOException on error
102      */
103     @Test
104     public void classFileDescriptorTestWithSpaces() throws ParseException, IOException
105     {
106         // Use a fixed zone for the whole test
107         TimeZone originalTz = TimeZone.getDefault();
108         try
109         {
110             TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
111 
112             var original = ResourceResolver.resolve("/org/djutils-test-resources/test folder/Test.class");
113             InputStream originalClassStream = original.openStream();
114             File cfdFile = copyToTempFile(originalClassStream, "Test", ".class");
115             var cfd = ResourceResolver.resolve(cfdFile.getAbsolutePath());
116 
117             // change the last accessed date
118             SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
119             formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
120             cfdFile.setLastModified(formatter.parse("2000-01-01 02:03:04").getTime());
121 
122             ClassFileDescriptor cfdClass = ClassUtil.classFileDescriptorForPath(cfd.asPath());
123             assertTrue(cfdClass.getName().startsWith("Test"));
124             assertTrue(cfdClass.getName().endsWith(".class"));
125             Date cfdClassDate = new Date(cfdClass.getLastChangedDate());
126             assertEquals("2000-01-01 02:03:04", formatter.format(cfdClassDate));
127             assertTrue(cfdClass.toString().startsWith("ClassFileDescriptor ["));
128 
129             var originalJar = ResourceResolver.resolve("/org/djutils-test-resources/test folder/Test.jar");
130             InputStream jarStream = originalJar.openStream();
131             File jarFile = copyToTempFile(jarStream, "Test", ".jar");
132             var jar = ResourceResolver.resolve(jarFile.getAbsolutePath());
133 
134             // change the last accessed date of the jar -- not of the file in the jar
135             jarFile.setLastModified(formatter.parse("2010-11-12 01:02:03").getTime());
136 
137             ClassFileDescriptor cfdJar = ClassUtil.classFileDescriptorForPath(jar.asPath());
138             Date cfdJarDate = new Date(cfdJar.getLastChangedDate());
139             assertTrue(cfdJar.getName().startsWith("Test"));
140             assertTrue(cfdJar.getName().endsWith(".jar"));
141             assertEquals("2010-11-12 01:02:03", formatter.format(cfdJarDate));
142 
143             // find the file in the jar
144             Path cfdJarFilePath = ResourceResolver.resolve(jar.asPath().toString() + "!/Test.class").asPath();
145             ClassFileDescriptor cfdJarFile = ClassUtil.classFileDescriptorForPath(cfdJarFilePath);
146             assertEquals("Test.class", cfdJarFile.getName());
147             Date cfdJarFileDate = new Date(cfdJarFile.getLastChangedDate());
148             assertTrue(formatter.format(cfdJarFileDate).startsWith("2000-01-01"));
149             // Note: time cannot be checked -- file was made in CET, test runs in UTC
150 
151             // if the file in the jar cannot be found, we should get the file descriptor of the jar itself
152             Path cfdJarFilePath2 = ResourceResolver.resolve(jar.asPath().toString() + "!/TestXYZ.class").asPath();
153             ClassFileDescriptor cfdJarFile2 = ClassUtil.classFileDescriptorForPath(cfdJarFilePath2);
154             assertTrue(cfdJarFile2.getName().startsWith("Test"));
155             assertTrue(cfdJarFile2.getName().endsWith(".jar"));
156             Date cfdJarFileDate2 = new Date(cfdJarFile2.getLastChangedDate());
157             assertEquals("2010-11-12 01:02:03", formatter.format(cfdJarFileDate2));
158         }
159         finally
160         {
161             TimeZone.setDefault(originalTz);
162         }
163     }
164 
165     /**
166      * Copy a (class) file to a temporary file.
167      * @param in the input stream of the file to copy
168      * @param prefix the prefix of the file (will be appended to make it unique)
169      * @param suffix the suffix of the file, including the 'dot'
170      * @return the file handle of the copied file
171      * @throws IOException on error
172      */
173     private static File copyToTempFile(final InputStream in, final String prefix, final String suffix) throws IOException
174     {
175         Path tempFile = Files.createTempFile(prefix, suffix);
176         tempFile.toFile().deleteOnExit();
177         try (OutputStream out = Files.newOutputStream(tempFile))
178         {
179             in.transferTo(out);
180         }
181         return tempFile.toFile();
182     }
183 }