001package org.andromda.core.metafacade; 002 003import java.io.Serializable; 004import java.net.URL; 005import java.util.ArrayList; 006import java.util.Arrays; 007import java.util.Collection; 008import java.util.LinkedHashMap; 009import java.util.List; 010import java.util.Map; 011import org.andromda.core.common.ClassUtils; 012import org.andromda.core.common.Constants; 013import org.andromda.core.common.ExceptionUtils; 014import org.andromda.core.common.ResourceUtils; 015import org.andromda.core.configuration.Namespaces; 016import org.andromda.core.namespace.NamespaceRegistry; 017import org.apache.commons.lang.StringUtils; 018 019/** 020 * Discovers all metafacade interfaces and implementation classes in each namespace registry. This class is 021 * then used to retrieve both the appropriate metafacade interface and/or metafacade implementation class based 022 * on one or the other. 023 * 024 * @author Chad Brandon 025 * @author Bob Fields 026 */ 027public class MetafacadeImpls 028 implements Serializable 029{ 030 private static final long serialVersionUID = 34L; 031 032 /** 033 * The shared instance. 034 */ 035 private static final MetafacadeImpls instance = new MetafacadeImpls(); 036 037 /** 038 * Stores each metafacade classes instance keyed by namespace. 039 */ 040 private final Map<String, MetafacadeClasses> metafacadeClasses = new LinkedHashMap<String, MetafacadeClasses>(); 041 042 /** 043 * Returns the shared instance of this class. 044 * 045 * @return MetafacadeImpls the shared instance. 046 */ 047 public static MetafacadeImpls instance() 048 { 049 return instance; 050 } 051 052 /** 053 * The current model type to which metafacade class retrieval applies. 054 */ 055 private String metafacadeModelNamespace; 056 057 /** 058 * Sets the current model type to which this instance's metafacade class retrieval 059 * should apply. 060 * 061 * @param metafacadeModelNamespace the namespace that has the metafacade model implementation. 062 */ 063 public void setMetafacadeModelNamespace(final String metafacadeModelNamespace) 064 { 065 this.metafacadeModelNamespace = metafacadeModelNamespace; 066 } 067 068 /** 069 * The extension for the metafacade implementation files. 070 */ 071 private static final String METAFACADE_IMPLEMENTATION_SUFFIX = 072 MetafacadeConstants.METAFACADE_IMPLEMENTATION_SUFFIX + ClassUtils.CLASS_EXTENSION; 073 074 /** 075 * Discovers and loads all metafacade implementation classes and interfaces in each available namespace registry into 076 * each given namespace in the <code>modelTypeNamespaces</code> list. 077 * Note that this method must be called before any metafacade implementation classes will be able to be retrieved 078 * when calling {@link #getMetafacadeClass(String)}or {@link #getMetafacadeImplClass(String)}. 079 * 080 * @param metafacadeModelNamespaces a list of each namespace containing a metafacade model facade implementation. 081 */ 082 public void discover(final String[] metafacadeModelNamespaces) 083 { 084 ExceptionUtils.checkNull( 085 "modelTypes", 086 metafacadeModelNamespaces); 087 final List<String> modelNamespaces = new ArrayList<String>(Arrays.asList(metafacadeModelNamespaces)); 088 for (final String modelNamespace : metafacadeModelNamespaces) 089 { 090 if (modelNamespace != null) 091 { 092 // - remove the current model type so that we don't keep out the namespace 093 // that stores the metafacade model 094 modelNamespaces.remove(modelNamespace); 095 096 MetafacadeClasses metafacadeClasses = this.metafacadeClasses.get(modelNamespace); 097 if (metafacadeClasses == null) 098 { 099 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}