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