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 }