View Javadoc
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 }