View Javadoc

1   /*
2    * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.properties.utils;
17  
18  import java.io.BufferedInputStream;
19  import java.io.File;
20  import java.io.FileInputStream;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.net.URL;
24  import java.util.jar.JarEntry;
25  import java.util.jar.JarFile;
26  
27  import org.apache.commons.io.IOUtils;
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  
31  /**
32   * Base implementation of class loaders that serve classes from the given
33   * directories.
34   *
35   * @author <a href="mailto:robert.reiner@smartics.de">Robert Reiner</a>
36   * @version $Revision:591 $
37   */
38  public abstract class AbstractProjectClassLoader extends ClassLoader
39  {
40    // ********************************* Fields *********************************
41  
42    // --- constants ------------------------------------------------------------
43  
44    // --- members --------------------------------------------------------------
45  
46    /**
47     * Reference to the logger for this class.
48     */
49    private final Logger log = LoggerFactory
50        .getLogger(AbstractProjectClassLoader.class);
51  
52    // ****************************** Initializer *******************************
53  
54    // ****************************** Constructors ******************************
55  
56    /**
57     * Default constructor.
58     *
59     * @param parent the parent class loader.
60     */
61    protected AbstractProjectClassLoader(final ClassLoader parent)
62    {
63      super(parent);
64    }
65  
66    // ****************************** Inner Classes *****************************
67  
68    // ********************************* Methods ********************************
69  
70    // --- init -----------------------------------------------------------------
71  
72    // --- get&set --------------------------------------------------------------
73  
74    // --- business -------------------------------------------------------------
75  
76    /**
77     * Ensures that the package of a class specified by the given name is already
78     * defined. If it is not defined the loaded class instance will not have a
79     * package reference.
80     *
81     * @param className the name of the class whose package is to check to be
82     *          already defined.
83     */
84    protected final void ensurePackageProvided(final String className)
85    {
86      final int lastDotIndex = className.lastIndexOf('.');
87      if (lastDotIndex != -1)
88      {
89        final String packageName = className.substring(0, lastDotIndex);
90        final Package paccage = getPackage(packageName);
91        if (paccage == null)
92        {
93          definePackage(packageName, null, null, null, null, null, null, null);
94        }
95      }
96    }
97  
98    /**
99     * Loads the class. Ensures that the package of the class is defined.
100    *
101    * @param className the name of the class.
102    * @param classFile the file with the binary data.
103    * @return the loaded class definition.
104    * @throws ClassNotFoundException if the class cannot be loaded.
105    */
106   protected final Class<?> loadClassFile(final String className,
107       final File classFile) throws ClassNotFoundException
108   {
109     ensurePackageProvided(className);
110     InputStream in = null;
111     try
112     {
113       in = new BufferedInputStream(new FileInputStream(classFile));
114       final byte[] data = IOUtils.toByteArray(in);
115       final Class<?> clazz = defineClass(className, data, 0, data.length);
116       return clazz;
117     }
118     catch (final IOException e)
119     {
120       throw createClassNotFoundException(className, classFile, e);
121     }
122     finally
123     {
124       IOUtils.closeQuietly(in);
125     }
126   }
127 
128   /**
129    * Opens a reader to the class file within the archive.
130    *
131    * @param className the name of the class to load.
132    * @param fileName the name of the class file to load.
133    * @param dirName the name of the directory the archive file is located.
134    * @return the reader to the source file for the given class in the archive.
135    * @throws ClassNotFoundException if the class cannot be found.
136    */
137   protected final Class<?> loadClassFromLibrary(final String className,
138       final String fileName, final File dirName) throws ClassNotFoundException
139   {
140     ensurePackageProvided(className);
141     try
142     {
143       final JarFile jarFile = new JarFile(dirName); // NOPMD
144       final JarEntry entry = jarFile.getJarEntry(fileName);
145       if (entry != null)
146       {
147         InputStream in = null;
148         try
149         {
150           in = new BufferedInputStream(jarFile.getInputStream(entry));
151           final byte[] data = IOUtils.toByteArray(in);
152           final Class<?> clazz = defineClass(className, data, 0, data.length);
153           return clazz;
154         }
155         finally
156         {
157           IOUtils.closeQuietly(in);
158         }
159       }
160     }
161     catch (final IOException e)
162     {
163       throw createClassNotFoundException(className, dirName, e);
164     }
165 
166     throw createClassNotFoundException(className, dirName, null);
167   }
168 
169   private ClassNotFoundException createClassNotFoundException(
170       final String className, final File dirName, final IOException rootCause)
171     throws ClassNotFoundException
172   {
173     final String message =
174         String.format("Cannot load class '%s' from file '%s'.", className,
175             dirName);
176     if (log.isDebugEnabled())
177     {
178       log.debug(message);
179     }
180 
181     return new ClassNotFoundException(message, rootCause);
182   }
183 
184   /**
185    * Opens a reader to the resource file within the archive.
186    *
187    * @param resourceName the name of the resource to load.
188    * @param fileName the name of the resource file to load.
189    * @param dirName the name of the directory the archive file is located.
190    * @return the reader to the source file for the given class in the archive.
191    * @throws IOException if the class cannot be found.
192    */
193   protected final URL loadResourceFromLibrary(final String resourceName,
194       final String fileName, final File dirName) throws IOException
195   {
196     ensurePackageProvided(resourceName);
197     try
198     {
199       return new URL("jar:file:" + dirName.getName() + "!/" + fileName);
200     }
201     catch (final IOException e)
202     {
203       final String message =
204           "Cannot load class '" + resourceName + "' from file '" + dirName
205               + "'.";
206       if (log.isDebugEnabled())
207       {
208         log.debug(message);
209       }
210       final IOException ioe = new IOException(message);
211       ioe.initCause(e);
212       throw ioe; // NOPMD
213     }
214   }
215 
216   // --- object basics --------------------------------------------------------
217 
218 }