1 package org.andromda.core.metafacade;
2
3 import java.io.Serializable;
4 import java.net.URL;
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.Collection;
8 import java.util.LinkedHashMap;
9 import java.util.List;
10 import java.util.Map;
11 import org.andromda.core.common.ClassUtils;
12 import org.andromda.core.common.Constants;
13 import org.andromda.core.common.ExceptionUtils;
14 import org.andromda.core.common.ResourceUtils;
15 import org.andromda.core.configuration.Namespaces;
16 import org.andromda.core.namespace.NamespaceRegistry;
17 import org.apache.commons.lang.StringUtils;
18
19 /**
20 * Discovers all metafacade interfaces and implementation classes in each namespace registry. This class is
21 * then used to retrieve both the appropriate metafacade interface and/or metafacade implementation class based
22 * on one or the other.
23 *
24 * @author Chad Brandon
25 * @author Bob Fields
26 */
27 public class MetafacadeImpls
28 implements Serializable
29 {
30 private static final long serialVersionUID = 34L;
31
32 /**
33 * The shared instance.
34 */
35 private static final MetafacadeImpls instance = new MetafacadeImpls();
36
37 /**
38 * Stores each metafacade classes instance keyed by namespace.
39 */
40 private final Map<String, MetafacadeClasses> metafacadeClasses = new LinkedHashMap<String, MetafacadeClasses>();
41
42 /**
43 * Returns the shared instance of this class.
44 *
45 * @return MetafacadeImpls the shared instance.
46 */
47 public static MetafacadeImpls instance()
48 {
49 return instance;
50 }
51
52 /**
53 * The current model type to which metafacade class retrieval applies.
54 */
55 private String metafacadeModelNamespace;
56
57 /**
58 * Sets the current model type to which this instance's metafacade class retrieval
59 * should apply.
60 *
61 * @param metafacadeModelNamespace the namespace that has the metafacade model implementation.
62 */
63 public void setMetafacadeModelNamespace(final String metafacadeModelNamespace)
64 {
65 this.metafacadeModelNamespace = metafacadeModelNamespace;
66 }
67
68 /**
69 * The extension for the metafacade implementation files.
70 */
71 private static final String METAFACADE_IMPLEMENTATION_SUFFIX =
72 MetafacadeConstants.METAFACADE_IMPLEMENTATION_SUFFIX + ClassUtils.CLASS_EXTENSION;
73
74 /**
75 * Discovers and loads all metafacade implementation classes and interfaces in each available namespace registry into
76 * each given namespace in the <code>modelTypeNamespaces</code> list.
77 * Note that this method must be called before any metafacade implementation classes will be able to be retrieved
78 * when calling {@link #getMetafacadeClass(String)}or {@link #getMetafacadeImplClass(String)}.
79 *
80 * @param metafacadeModelNamespaces a list of each namespace containing a metafacade model facade implementation.
81 */
82 public void discover(final String[] metafacadeModelNamespaces)
83 {
84 ExceptionUtils.checkNull(
85 "modelTypes",
86 metafacadeModelNamespaces);
87 final List<String> modelNamespaces = new ArrayList<String>(Arrays.asList(metafacadeModelNamespaces));
88 for (final String modelNamespace : metafacadeModelNamespaces)
89 {
90 if (modelNamespace != null)
91 {
92 // - remove the current model type so that we don't keep out the namespace
93 // that stores the metafacade model
94 modelNamespaces.remove(modelNamespace);
95
96 MetafacadeClasses metafacadeClasses = this.metafacadeClasses.get(modelNamespace);
97 if (metafacadeClasses == null)
98 {
99 metafacadeClasses = new MetafacadeClasses();
100 this.metafacadeClasses.put(
101 modelNamespace,
102 metafacadeClasses);
103 }
104 metafacadeClasses.clear();
105 try
106 {
107 final Namespaces namespacesConfiguration = Namespaces.instance();
108 for (final NamespaceRegistry namespaceRegistry : namespacesConfiguration.getNamespaceRegistries())
109 {
110 final String namespaceRegistryName = namespaceRegistry.getName();
111 if (!modelNamespaces.contains(namespaceRegistryName))
112 {
113 this.registerMetafacadeClasses(
114 metafacadeClasses,
115 namespacesConfiguration,
116 namespaceRegistry);
117 }
118 }
119 }
120 catch (final Throwable throwable)
121 {
122 throw new MetafacadeImplsException(throwable);
123 }
124
125 // - add the metafacade model namespace back
126 modelNamespaces.add(modelNamespace);
127 }
128 }
129 }
130
131 /**
132 * Registers the metafacade classes for the given <code>namespaceRegistry</code>.
133 *
134 * @param metafacadeClasses the metafacade classes instance to store the registered metafacade classes.
135 * @param namespaces the namespaces from which we retrieve the additional namespace information.
136 * @param namespaceRegistry the registry from which we retrieve the classes.
137 */
138 private void registerMetafacadeClasses(
139 final MetafacadeClasses metafacadeClasses,
140 final Namespaces namespaces,
141 final NamespaceRegistry namespaceRegistry)
142 {
143 final String namespaceRegistryName = namespaceRegistry.getName();
144 if (namespaces.isComponentPresent(
145 namespaceRegistryName,
146 Constants.COMPONENT_METAFACADES))
147 {
148 final URL[] namespaceRoots = namespaceRegistry.getResourceRoots();
149 if (namespaceRoots != null && namespaceRoots.length > 0)
150 {
151 for (final URL namespaceRoot : namespaceRoots)
152 {
153 final Collection<String> contents = ResourceUtils.getDirectoryContents(
154 namespaceRoot,
155 false,
156 null);
157 for (final String path : contents)
158 {
159 if (path.endsWith(METAFACADE_IMPLEMENTATION_SUFFIX))
160 {
161 final String typeName =
162 StringUtils.replace(
163 ResourceUtils.normalizePath(path).replace(
164 '/',
165 '.'),
166 ClassUtils.CLASS_EXTENSION,
167 "");
168 Class implementationClass = null;
169 try
170 {
171 implementationClass = ClassUtils.loadClass(typeName);
172 }
173 catch (final Exception exception)
174 {
175 // - ignore
176 }
177 if (implementationClass != null &&
178 MetafacadeBase.class.isAssignableFrom(implementationClass))
179 {
180 final List<Class> allInterfaces = ClassUtils.getInterfaces(implementationClass);
181 if (!allInterfaces.isEmpty())
182 {
183 final Class interfaceClass = allInterfaces.iterator().next();
184 final String implementationClassName = implementationClass.getName();
185 final String interfaceClassName = interfaceClass.getName();
186 metafacadeClasses.metafacadesByImpls.put(
187 implementationClassName,
188 interfaceClassName);
189 metafacadeClasses.implsByMetafacades.put(
190 interfaceClassName,
191 implementationClassName);
192 }
193 }
194 }
195 }
196 }
197 }
198 }
199 }
200
201 /**
202 * Attempts to retrieve the metafacade classes instance with the current active namespace
203 * and throws an exception if one can not be found.
204 *
205 * @return the metafacade classes instance.
206 */
207 private MetafacadeClasses getMetafacadeClasses()
208 {
209 final MetafacadeClasses classes = this.metafacadeClasses.get(this.metafacadeModelNamespace);
210 if (classes == null)
211 {
212 throw new MetafacadeImplsException("Namespace '" + this.metafacadeModelNamespace + "' is not a registered metafacade model facade namespace");
213 }
214 return classes;
215 }
216
217 /**
218 * Retrieves the metafacade class from the passed in <code>metafacadeImplClass</code>. Will return a
219 * MetafacadeImplsException if a metafacade class can not be found for the <code>metafacadeImplClass</code>
220 *
221 * @param metafacadeImplClass the name of the metafacade implementation class.
222 * @return the metafacade Class
223 */
224 public Class getMetafacadeClass(final String metafacadeImplClass)
225 {
226 ExceptionUtils.checkEmpty(
227 "metafacadeImplClass",
228 metafacadeImplClass);
229 return this.getMetafacadeClasses().getMetafacadeClass(metafacadeImplClass);
230 }
231
232 /**
233 * Retrieves the metafacade implementation class from the passed in <code>metafacadeClass</code>. Will return a
234 * MetafacadeImplsException if a metafacade implementation class can not be found for the
235 * <code>metafacadeClass</code>
236 *
237 * @param metafacadeClass the name of the metafacade class.
238 * @return the metafacade implementation Class
239 */
240 public Class getMetafacadeImplClass(final String metafacadeClass)
241 {
242 ExceptionUtils.checkEmpty(
243 "metafacadeClass",
244 metafacadeClass);
245 return this.getMetafacadeClasses().getMetafacadeImplClass(metafacadeClass);
246 }
247
248 /**
249 * Stores the metafacade interface and implementation classes.
250 */
251 static final class MetafacadeClasses
252 {
253 /**
254 * Stores all <code>metafacade</code> implementation classes keyed by <code>metafacade</code> interface class.
255 */
256 Map<String, String> implsByMetafacades = new LinkedHashMap<String, String>();
257
258 /**
259 * Stores all <code>metafacade</code> interface classes keyed by <code>metafacade</code> implementation class.
260 */
261 Map<String, String> metafacadesByImpls = new LinkedHashMap<String, String>();
262
263 /**
264 * Retrieves the metafacade class from the passed in <code>metafacadeImplClass</code>. Will return a
265 * MetafacadeImplsException if a metafacade class can not be found for the <code>metafacadeImplClass</code>
266 *
267 * @param metafacadeImplClass the name of the metafacade implementation class.
268 * @return the metafacade Class
269 */
270 Class getMetafacadeClass(final String metafacadeImplClass)
271 {
272 ExceptionUtils.checkEmpty(
273 "metafacadeImplClass",
274 metafacadeImplClass);
275 Class metafacadeClass = null;
276 try
277 {
278 final String metafacadeClassName = this.metafacadesByImpls.get(metafacadeImplClass);
279 if (StringUtils.isEmpty(metafacadeClassName))
280 {
281 throw new MetafacadeImplsException("Can not find a metafacade interface for --> '" +
282 metafacadeImplClass + "', check your classpath");
283 }
284 metafacadeClass = ClassUtils.loadClass(metafacadeClassName);
285 }
286 catch (final Throwable throwable)
287 {
288 throw new MetafacadeImplsException(throwable);
289 }
290 return metafacadeClass;
291 }
292
293 /**
294 * Retrieves the metafacade implementation class from the passed in <code>metafacadeClass</code>. Will return a
295 * MetafacadeImplsException if a metafacade implementation class can not be found for the
296 * <code>metafacadeClass</code>
297 *
298 * @param metafacadeClass the name of the metafacade class.
299 * @return the metafacade implementation Class
300 */
301 Class getMetafacadeImplClass(final String metafacadeClass)
302 {
303 ExceptionUtils.checkEmpty(
304 "metafacadeClass",
305 metafacadeClass);
306 Class metafacadeImplementationClass = null;
307 try
308 {
309 final String metafacadeImplementationClassName = this.implsByMetafacades.get(metafacadeClass);
310 if (StringUtils.isEmpty(metafacadeImplementationClassName))
311 {
312 throw new MetafacadeImplsException("Can not find a metafacade implementation class for --> '" +
313 metafacadeClass + "' check your classpath");
314 }
315 metafacadeImplementationClass = ClassUtils.loadClass(metafacadeImplementationClassName);
316 }
317 catch (final Throwable throwable)
318 {
319 throw new MetafacadeImplsException(throwable);
320 }
321 return metafacadeImplementationClass;
322 }
323
324 /**
325 * Clears each map of any classes it contains.
326 */
327 void clear()
328 {
329 this.metafacadesByImpls.clear();
330 this.implsByMetafacades.clear();
331 }
332
333 /**
334 * @see Object#toString()
335 */
336 public String toString()
337 {
338 return super.toString() + '[' + this.metafacadesByImpls + ']';
339 }
340 }
341 }