View Javadoc

1   /*
2    * Copyright 2006-2016 The JGUIraffe Team.
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 net.sf.jguiraffe.di.impl;
17  
18  import java.util.Collections;
19  import java.util.Map;
20  import java.util.Set;
21  import java.util.concurrent.ConcurrentHashMap;
22  
23  import net.sf.jguiraffe.di.ClassLoaderProvider;
24  import net.sf.jguiraffe.di.InjectionException;
25  import net.sf.jguiraffe.di.ReflectionUtils;
26  
27  /**
28   * <p>
29   * A default implementation of the {@code ClassLoaderProvider} interface.
30   * </p>
31   * <p>
32   * This is a straight-forward (but thread-safe) implementation of all the
33   * methods defined by the {@code ClassLoaderProvider} interface. Class loaders
34   * registered at this object are stored in a map. A default class loader name
35   * can be set. If none has been set, the class loader that loaded this class is
36   * returned as default class loader.
37   * </p>
38   * <p>
39   * Per default, classes of the library (starting with the prefix
40   * {@code net.sf.jguiraffe}) are always loaded by the class loader which loaded
41   * this class. This makes sense for instance in an OSGi environment: it
42   * allows access even to internal implementation classes. This behavior can be
43   * disabled by setting the {@code handleInternalClasses} property to
44   * <b>false</b>. Then the selected class loader is used to load all classes.
45   * </p>
46   *
47   * @author Oliver Heger
48   * @version $Id: DefaultClassLoaderProvider.java 211 2012-07-10 19:49:13Z oheger $
49   */
50  public class DefaultClassLoaderProvider implements ClassLoaderProvider
51  {
52      /** The prefix for class names which belong to this library. */
53      private static final String LIBRARY_CLASS_PREFIX = "net.sf.jguiraffe.";
54  
55      /** A map with the class loaders registered at this object. */
56      private final Map<String, ClassLoader> mapLoaders;
57  
58      /** Stores the default class loader name. */
59      private volatile String defaultLoaderName;
60  
61      /** A flag whether internal classes should be treated in a special way. */
62      private final boolean handleInternalClasses;
63  
64      /**
65       * Creates a new instance of {@code DefaultClassLoaderProvider}.
66       */
67      public DefaultClassLoaderProvider()
68      {
69          this(true);
70      }
71  
72      /**
73       * Creates a new instance of {@code DefaultClassLoaderProvider} and sets the
74       * flag whether internal classes should be handled in a special way.
75       *
76       * @param fHandleInternalClasses the value of the flag
77       * @since 1.2
78       */
79      public DefaultClassLoaderProvider(boolean fHandleInternalClasses)
80      {
81          mapLoaders = new ConcurrentHashMap<String, ClassLoader>();
82          handleInternalClasses = fHandleInternalClasses;
83      }
84  
85      /**
86       * Returns a flag whether internal library classes are handled in a special
87       * way by this class loader provider. If this property is <b>true</b>, all
88       * classes belonging to this library are loaded by the class loader which
89       * also loaded this class.
90       *
91       * @return <b>true</b> if library classes are handled in a special way,
92       *         <b>false</b> otherwise
93       * @since 1.2
94       */
95      public boolean isHandleInternalClasses()
96      {
97          return handleInternalClasses;
98      }
99  
100     /**
101      * Returns a set with the names of the class loaders that have been
102      * registered at this object. All these names can be passed into the {@code
103      * getClassLoader()} method.
104      *
105      * @return a set with the names of the class loaders registered at this
106      *         object
107      */
108     public Set<String> classLoaderNames()
109     {
110         return Collections.unmodifiableSet(mapLoaders.keySet());
111     }
112 
113     /**
114      * Returns the class loader specified by the given symbolic name. This
115      * method supports all variants: the default class loader (in this case the
116      * name is <b>null</b>), the context class loader, and a registered class
117      * loader.
118      *
119      * @param name the name of the class loader
120      * @return the corresponding class loader
121      * @throws InjectionException if the class loader cannot be resolved
122      */
123     public ClassLoader getClassLoader(String name)
124     {
125         String loaderName = (name != null) ? name : getDefaultClassLoaderName();
126         if (loaderName == null)
127         {
128             return getClass().getClassLoader();
129         }
130 
131         if (CONTEXT_CLASS_LOADER.equals(loaderName))
132         {
133             return Thread.currentThread().getContextClassLoader();
134         }
135 
136         ClassLoader cl = mapLoaders.get(loaderName);
137         if (cl == null)
138         {
139             throw new InjectionException("ClassLoader is not registered: "
140                     + name);
141         }
142         return cl;
143     }
144 
145     /**
146      * Returns the name of the default class loader. Result can be <b>null</b>
147      * if no default class loader name has been set so far.
148      *
149      * @return the name of the default class loader
150      */
151     public String getDefaultClassLoaderName()
152     {
153         return defaultLoaderName;
154     }
155 
156     /**
157      * Loads the class with the specified name using the class loader identified
158      * by the given symbolic reference.
159      *
160      * @param name the class of the name to be loaded
161      * @param loaderRef determines the class loader to be used
162      * @return the loaded class
163      * @throws InjectionException if the class cannot be loaded
164      */
165     public Class<?> loadClass(String name, String loaderRef)
166     {
167         return ReflectionUtils.loadClass(name,
168                 determineClassLoader(name, loaderRef));
169     }
170 
171     /**
172      * Allows to register a class loader under a symbolic name.
173      *
174      * @param name the name of the class loader (must not be <b>null</b>)
175      * @param loader the class loader to be registered; can be <b>null</b>, then
176      *        the class loader with the given name will be removed
177      * @throws IllegalArgumentException if the name is <b>null</b>
178      */
179     public void registerClassLoader(String name, ClassLoader loader)
180     {
181         if (name == null)
182         {
183             throw new IllegalArgumentException("Name must not be null!");
184         }
185 
186         if (loader == null)
187         {
188             mapLoaders.remove(name);
189         }
190         else
191         {
192             mapLoaders.put(name, loader);
193         }
194     }
195 
196     /**
197      * Sets the name of the default class loader. Applications can here specify
198      * the name of a class loader they have registered or the reserved name
199      * {@link ClassLoaderProvider#CONTEXT_CLASS_LOADER} for the context class
200      * loader. A value of <b>null</b> causes {@link #getClassLoader(String)} to
201      * fall back to the class loader which loaded this class.
202      *
203      * @param loaderName the new default class loader
204      */
205     public void setDefaultClassLoaderName(String loaderName)
206     {
207         defaultLoaderName = loaderName;
208     }
209 
210     /**
211      * Obtains the class loader for loading the specified class.
212      *
213      * @param clsName the class name
214      * @param loaderRef the reference to the class loader
215      * @return the class loader for loading this class
216      * @throws IllegalArgumentException if the class name is <b>null</b>
217      */
218     private ClassLoader determineClassLoader(String clsName, String loaderRef)
219     {
220         return (isHandleInternalClasses() && clsName != null && clsName
221                 .startsWith(LIBRARY_CLASS_PREFIX)) ? getClass()
222                 .getClassLoader() : getClassLoader(loaderRef);
223     }
224 }