1 /*
2  * JasperReports - Free Java Reporting Library.
3  * Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved.
4  * http://www.jaspersoft.com
5  *
6  * Unless you have purchased a commercial license agreement from Jaspersoft,
7  * the following license terms apply:
8  *
9  * This program is part of JasperReports.
10  *
11  * JasperReports is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * JasperReports is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
23  */

24 package net.sf.jasperreports.engine.util;
25
26 import java.io.ByteArrayOutputStream;
27 import java.io.File;
28 import java.io.FileInputStream;
29 import java.io.IOException;
30 import java.security.ProtectionDomain;
31 import java.util.HashMap;
32 import java.util.Map;
33
34 import net.sf.jasperreports.engine.JRRuntimeException;
35
36 /**
37  * @author Teodor Danciu (teodord@users.sourceforge.net)
38  */

39 public class JRClassLoader extends ClassLoader
40 {
41     
42     private static final Map<String, String> PRIMITIVE_COMPONENT_ENCODING;
43     static
44     {
45         PRIMITIVE_COMPONENT_ENCODING = new HashMap<>();
46         //taken from https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getName--
47         PRIMITIVE_COMPONENT_ENCODING.put(Boolean.TYPE.getName(), "Z");
48         PRIMITIVE_COMPONENT_ENCODING.put(Byte.TYPE.getName(), "B");
49         PRIMITIVE_COMPONENT_ENCODING.put(Character.TYPE.getName(), "C");
50         PRIMITIVE_COMPONENT_ENCODING.put(Double.TYPE.getName(), "D");
51         PRIMITIVE_COMPONENT_ENCODING.put(Float.TYPE.getName(), "F");
52         PRIMITIVE_COMPONENT_ENCODING.put(Integer.TYPE.getName(), "I");
53         PRIMITIVE_COMPONENT_ENCODING.put(Long.TYPE.getName(), "J");
54         PRIMITIVE_COMPONENT_ENCODING.put(Short.TYPE.getName(), "S");
55     }
56
57     private static ProtectionDomainFactory protectionDomainFactory;
58     
59     public static synchronized ProtectionDomainFactory getProtectionDomainFactory()
60     {
61         if (protectionDomainFactory == null)
62         {
63             protectionDomainFactory = new SingleProtectionDomainFactory(JRClassLoader.class.getProtectionDomain());
64         }
65
66         return protectionDomainFactory;
67     }
68     
69     /**
70      * Sets the protection to be used for classes loaded via
71      * the {@link #loadClassFromBytes(String, byte[]) loadClassFromBytes} method.
72      *
73      * By default, the protection domain of this class is used for the loaded classes. 
74      * 
75      * @param protectionDomain the protection domain to be used
76      * @see #loadClassFromBytes(String, byte[])
77      */

78     public static void setProtectionDomain(ProtectionDomain protectionDomain)
79     {
80         SingleProtectionDomainFactory factory = new SingleProtectionDomainFactory(protectionDomain);
81         setProtectionDomainFactory(factory);
82     }
83     
84     /**
85      * Sets a protection domain factory to be used for creating protection domains
86      * for the classes loaded by instances of this class.
87      * <p>
88      * For every instance of this class,
89      * {@link ProtectionDomainFactory#getProtectionDomain(ClassLoader) getProtectionDomain} is called
90      * and the resulting protection domain is used when loading classes through the newly created
91      * classloader.
92      * 
93      * @param protectionDomainFactory the protection domain factory.
94      * @see ProtectionDomainFactory#getProtectionDomain(ClassLoader)
95      */

96     public static void setProtectionDomainFactory(ProtectionDomainFactory protectionDomainFactory)
97     {
98         JRClassLoader.protectionDomainFactory = protectionDomainFactory;
99     }
100     
101     private ProtectionDomain protectionDomain;
102     
103     private ClassLoaderFilter classLoaderFilter;
104     
105     /**
106      *
107      */

108     protected JRClassLoader()
109     {
110         super();
111     }
112
113     protected JRClassLoader(ClassLoaderFilter classLoaderFilter)
114     {
115         super();
116         
117         this.classLoaderFilter = classLoaderFilter;
118     }
119
120     /**
121      *
122      */

123     protected JRClassLoader(ClassLoader parent)
124     {
125         super(parent);
126     }
127
128     protected JRClassLoader(ClassLoader parent, ClassLoaderFilter classLoaderFilter)
129     {
130         super(parent);
131         
132         this.classLoaderFilter = classLoaderFilter;
133     }
134
135
136     /**
137      *
138      */

139     public static Class<?> loadClassForName(String className) throws ClassNotFoundException
140     {
141         Class<?> clazz = null;
142
143         String classRealName = className;
144         ClassNotFoundException initialEx = null;
145
146         try
147         {
148             clazz = loadClassForRealName(classRealName);
149         }
150         catch (ClassNotFoundException e)
151         {
152             initialEx = e;
153         }
154         
155         int lastDotIndex = 0;
156         while (clazz == null && (lastDotIndex = classRealName.lastIndexOf('.')) > 0)
157         {
158             classRealName = 
159                 classRealName.substring(0, lastDotIndex) + "$" + classRealName.substring(lastDotIndex + 1);
160             try
161             {
162                 clazz = loadClassForRealName(classRealName);
163             }
164             catch (ClassNotFoundException e)
165             {
166             }
167         }
168         
169         if (clazz == null)
170         {
171             throw initialEx;
172         }
173         
174         return clazz;
175     }
176
177
178     /**
179      *
180      */

181     public static Class<?> loadClassForRealName(String className) throws ClassNotFoundException
182     {
183         Class<?> clazz = null;
184
185         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
186         if (classLoader != null)
187         {
188             try
189             {
190                 clazz = Class.forName(className, true, classLoader);
191             }
192             catch (ClassNotFoundException e)
193             {
194                 //if (log.isWarnEnabled())
195                 //    log.warn("Failure using Thread.currentThread().getContextClassLoader() in JRClassLoader class. Using JRClassLoader.class.getClassLoader() instead.");
196             }
197         }
198
199         if (clazz == null)
200         {
201             classLoader = JRClassLoader.class.getClassLoader();
202             if (classLoader == null)
203             {
204                 clazz = Class.forName(className);
205             }
206             else
207             {
208                 clazz = Class.forName(className, true, classLoader);
209             }
210         }
211
212         return clazz;
213     }
214
215
216     /**
217      *
218      */

219     public static Class<?> loadClassFromFile(String className, File file) throws IOException
220     {
221         Class<?> clazz = null;
222
223         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
224         if (classLoader != null)
225         {
226             try
227             {
228                 clazz = 
229                     (new JRClassLoader(classLoader))
230                         .loadClass(className, file);
231             }
232             catch(NoClassDefFoundError e)
233             {
234                 //if (log.isWarnEnabled())
235                 //    log.warn("Failure using Thread.currentThread().getContextClassLoader() in JRClassLoader class. Using JRClassLoader.class.getClassLoader() instead.");
236             }
237         }
238     
239         if (clazz == null)
240         {
241             classLoader = JRClassLoader.class.getClassLoader();
242             if (classLoader == null)
243             {
244                 clazz = 
245                     (new JRClassLoader())
246                         .loadClass(className, file);
247             }
248             else
249             {
250                 clazz = 
251                     (new JRClassLoader(classLoader))
252                         .loadClass(className, file);
253             }
254         }
255         
256         return clazz;
257     }
258
259
260     /**
261      *
262      */

263     public static Class<?> loadClassFromBytes(String className, byte[] bytecodes)
264     {
265         return loadClassFromBytes(null, className, bytecodes);
266     }
267     
268     public static Class<?> loadClassFromBytes(ClassLoaderFilter classLoaderFilter, 
269             String className, byte[] bytecodes)
270     {
271         Class<?> clazz = null;
272
273         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
274         if (classLoader != null)
275         {
276             try
277             {
278                 clazz = 
279                     (new JRClassLoader(classLoader, classLoaderFilter))
280                         .loadClass(className, bytecodes);
281             }
282             catch(NoClassDefFoundError e)
283             {
284                 //if (log.isWarnEnabled())
285                 //    log.warn("Failure using Thread.currentThread().getContextClassLoader() in JRClassLoader class. Using JRClassLoader.class.getClassLoader() instead.");
286             }
287         }
288     
289         if (clazz == null)
290         {
291             classLoader = JRClassLoader.class.getClassLoader();
292             if (classLoader == null)
293             {
294                 clazz = 
295                     (new JRClassLoader(classLoaderFilter))
296                         .loadClass(className, bytecodes);
297             }
298             else
299             {
300                 clazz = 
301                     (new JRClassLoader(classLoader, classLoaderFilter))
302                         .loadClass(className, bytecodes);
303             }
304         }
305
306         return clazz;
307     }
308
309
310     /**
311      *
312      */

313     protected Class<?> loadClass(String className, File file) throws IOException
314     {
315         FileInputStream fis = null;
316         ByteArrayOutputStream baos = null;
317
318         byte[] bytecodes = new byte[10000];
319         int ln = 0;
320
321         try
322         {
323             fis = new FileInputStream(file);
324             baos = new ByteArrayOutputStream();
325
326             while ( (ln = fis.read(bytecodes)) > 0 )
327             {
328                 baos.write(bytecodes, 0, ln);
329             }
330
331             baos.flush();
332         }
333         finally
334         {
335             if (baos != null)
336             {
337                 try
338                 {
339                     baos.close();
340                 }
341                 catch(IOException e)
342                 {
343                 }
344             }
345
346             if (fis != null)
347             {
348                 try
349                 {
350                     fis.close();
351                 }
352                 catch(IOException e)
353                 {
354                 }
355             }
356         }
357
358         return loadClass(className, baos.toByteArray());
359     }
360
361     protected synchronized ProtectionDomain getProtectionDomain()
362     {
363         if (protectionDomain == null)
364         {
365             protectionDomain = getProtectionDomainFactory().getProtectionDomain(this);
366         }
367         return protectionDomain;
368     }
369     
370     /**
371      *
372      */

373     protected Class<?> loadClass(String className, byte[] bytecodes)
374     {
375         Class<?> clazz = null;
376
377         clazz = 
378             defineClass(
379                 className, 
380                 bytecodes, 
381                 0, 
382                 bytecodes.length,
383                 getProtectionDomain()
384                 );
385
386         return clazz;
387     }
388
389     @Override
390     protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException, JRRuntimeException
391     {
392         if (classLoaderFilter != null)
393         {
394             classLoaderFilter.checkClassVisibility(name);
395         }
396         
397         return super.loadClass(name, resolve);
398     }
399
400     /**
401      *
402      */

403     public static String getClassRealName(String className)
404     {
405         if (className == null)
406         {
407             return null;
408         }
409         
410         int ltPos = className.indexOf('<');
411         if (ltPos > 0)
412         {
413             int gtPos = className.lastIndexOf('>');
414             if (gtPos > ltPos)
415             {
416                 className = className.substring(0, ltPos) + className.substring(gtPos);
417             }
418         }
419         
420         int arrayDimension = 0;
421         int classNameEnd = className.length();
422         int index = 0;
423         int pos = 0;
424         while (index < classNameEnd && (pos = className.indexOf('[', index)) >= 0)
425         {
426             if (index == 0)
427             {
428                 classNameEnd = pos;
429             }
430             index = pos;
431             arrayDimension++;
432         }
433
434         if (arrayDimension > 0)
435         {
436             StringBuilder sb = new StringBuilder();
437             
438             for(int i = 0; i < arrayDimension; i++)
439             {
440                 sb.append('[');
441             }
442             
443             String componentClass = className.substring(0, classNameEnd);
444             String primitiveEncoding = PRIMITIVE_COMPONENT_ENCODING.get(componentClass);
445             if (primitiveEncoding == null)
446             {
447                 //non primitive component
448                 sb.append('L');
449                 sb.append(componentClass);
450                 sb.append(';');
451             }
452             else
453             {
454                 sb.append(primitiveEncoding);
455             }
456
457             return sb.toString();
458         }
459         
460         return className;
461     }
462
463
464 }
465