View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.beanutils2.converters;
19  
20  import java.io.ByteArrayOutputStream;
21  import java.io.FileNotFoundException;
22  import java.io.IOException;
23  import java.io.InputStream;
24  
25  /**
26   * A special classloader useful for testing j2ee-like scenarios.
27   *
28   * <p>
29   * In some tests we want to be able to emulate "container" frameworks, where code runs in a hierarchy of class loaders, and certain classes may be loaded by
30   * various class loaders in the hierarchy.
31   * </p>
32   *
33   * <p>
34   * Normally this is done by having certain jars or class-file-directories in the classpath of some class loaders but not others. This is quite difficult to
35   * integrate with the build process for the unit tests though; compiling certain classes and having the output go into places that is not in the default
36   * classpath for the unit tests would be a major pain.
37   * </p>
38   *
39   * <p>
40   * So this class takes a sneaky alternative approach: it can grab any class already loaded by a parent classloader and <em>reload</em> that class via this
41   * classloader. The effect is exactly as if a class (or jar file) had been present in the classpath for a container's "shared" classloader <em>and</em> been
42   * present in the component-specific classpath too, without any messing about with the way unit test code is compiled or executed.
43   */
44  public class ClassReloader extends ClassLoader {
45  
46      public ClassReloader(final ClassLoader parent) {
47          super(parent);
48      }
49  
50      /**
51       * Given a class already in the classpath of a parent classloader, reload that class via this classloader.
52       */
53      public Class<?> reload(final Class<?> clazz) throws FileNotFoundException, IOException {
54          final String className = clazz.getName();
55          final String classFile = className.replace('.', '/') + ".class";
56          final ByteArrayOutputStream baos = new ByteArrayOutputStream();
57          try (InputStream classStream = getParent().getResourceAsStream(classFile)) {
58  
59              if (classStream == null) {
60                  throw new FileNotFoundException(classFile);
61              }
62  
63              final byte[] buf = new byte[1024];
64              for (;;) {
65                  final int bytesRead = classStream.read(buf);
66                  if (bytesRead == -1) {
67                      break;
68                  }
69                  baos.write(buf, 0, bytesRead);
70              }
71          }
72  
73          final byte[] classData = baos.toByteArray();
74  
75          // now we have the raw class data, let's turn it into a class
76          final Class<?> newClass = defineClass(className, classData, 0, classData.length);
77          resolveClass(newClass);
78          return newClass;
79      }
80  }