View Javadoc

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                  // - remove the current model type so that we don't keep out the namespace
94                  //   that stores the metafacade model
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                 // - add the metafacade model namespace back
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                                 // - ignore
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 }