MetafacadeImpls.java
package org.andromda.core.metafacade;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.andromda.core.common.ClassUtils;
import org.andromda.core.common.Constants;
import org.andromda.core.common.ExceptionUtils;
import org.andromda.core.common.ResourceUtils;
import org.andromda.core.configuration.Namespaces;
import org.andromda.core.namespace.NamespaceRegistry;
import org.apache.commons.lang.StringUtils;
/**
* Discovers all metafacade interfaces and implementation classes in each namespace registry. This class is
* then used to retrieve both the appropriate metafacade interface and/or metafacade implementation class based
* on one or the other.
*
* @author Chad Brandon
* @author Bob Fields
*/
public class MetafacadeImpls
implements Serializable
{
private static final long serialVersionUID = 34L;
/**
* The shared instance.
*/
private static final MetafacadeImpls instance = new MetafacadeImpls();
/**
* Stores each metafacade classes instance keyed by namespace.
*/
private final Map<String, MetafacadeClasses> metafacadeClasses = new LinkedHashMap<String, MetafacadeClasses>();
/**
* Returns the shared instance of this class.
*
* @return MetafacadeImpls the shared instance.
*/
public static MetafacadeImpls instance()
{
return instance;
}
/**
* The current model type to which metafacade class retrieval applies.
*/
private String metafacadeModelNamespace;
/**
* Sets the current model type to which this instance's metafacade class retrieval
* should apply.
*
* @param metafacadeModelNamespace the namespace that has the metafacade model implementation.
*/
public void setMetafacadeModelNamespace(final String metafacadeModelNamespace)
{
this.metafacadeModelNamespace = metafacadeModelNamespace;
}
/**
* The extension for the metafacade implementation files.
*/
private static final String METAFACADE_IMPLEMENTATION_SUFFIX =
MetafacadeConstants.METAFACADE_IMPLEMENTATION_SUFFIX + ClassUtils.CLASS_EXTENSION;
/**
* Discovers and loads all metafacade implementation classes and interfaces in each available namespace registry into
* each given namespace in the <code>modelTypeNamespaces</code> list.
* Note that this method must be called before any metafacade implementation classes will be able to be retrieved
* when calling {@link #getMetafacadeClass(String)}or {@link #getMetafacadeImplClass(String)}.
*
* @param metafacadeModelNamespaces a list of each namespace containing a metafacade model facade implementation.
*/
public void discover(final String[] metafacadeModelNamespaces)
{
ExceptionUtils.checkNull(
"modelTypes",
metafacadeModelNamespaces);
final List<String> modelNamespaces = new ArrayList<String>(Arrays.asList(metafacadeModelNamespaces));
for (final String modelNamespace : metafacadeModelNamespaces)
{
if (modelNamespace != null)
{
// - remove the current model type so that we don't keep out the namespace
// that stores the metafacade model
modelNamespaces.remove(modelNamespace);
MetafacadeClasses metafacadeClasses = this.metafacadeClasses.get(modelNamespace);
if (metafacadeClasses == null)
{
metafacadeClasses = new MetafacadeClasses();
this.metafacadeClasses.put(
modelNamespace,
metafacadeClasses);
}
metafacadeClasses.clear();
try
{
final Namespaces namespacesConfiguration = Namespaces.instance();
for (final NamespaceRegistry namespaceRegistry : namespacesConfiguration.getNamespaceRegistries())
{
final String namespaceRegistryName = namespaceRegistry.getName();
if (!modelNamespaces.contains(namespaceRegistryName))
{
this.registerMetafacadeClasses(
metafacadeClasses,
namespacesConfiguration,
namespaceRegistry);
}
}
}
catch (final Throwable throwable)
{
throw new MetafacadeImplsException(throwable);
}
// - add the metafacade model namespace back
modelNamespaces.add(modelNamespace);
}
}
}
/**
* Registers the metafacade classes for the given <code>namespaceRegistry</code>.
*
* @param metafacadeClasses the metafacade classes instance to store the registered metafacade classes.
* @param namespaces the namespaces from which we retrieve the additional namespace information.
* @param namespaceRegistry the registry from which we retrieve the classes.
*/
private void registerMetafacadeClasses(
final MetafacadeClasses metafacadeClasses,
final Namespaces namespaces,
final NamespaceRegistry namespaceRegistry)
{
final String namespaceRegistryName = namespaceRegistry.getName();
if (namespaces.isComponentPresent(
namespaceRegistryName,
Constants.COMPONENT_METAFACADES))
{
final URL[] namespaceRoots = namespaceRegistry.getResourceRoots();
if (namespaceRoots != null && namespaceRoots.length > 0)
{
for (final URL namespaceRoot : namespaceRoots)
{
final Collection<String> contents = ResourceUtils.getDirectoryContents(
namespaceRoot,
false,
null);
for (final String path : contents)
{
if (path.endsWith(METAFACADE_IMPLEMENTATION_SUFFIX))
{
final String typeName =
StringUtils.replace(
ResourceUtils.normalizePath(path).replace(
'/',
'.'),
ClassUtils.CLASS_EXTENSION,
"");
Class implementationClass = null;
try
{
implementationClass = ClassUtils.loadClass(typeName);
}
catch (final Exception exception)
{
// - ignore
}
if (implementationClass != null &&
MetafacadeBase.class.isAssignableFrom(implementationClass))
{
final List<Class> allInterfaces = ClassUtils.getInterfaces(implementationClass);
if (!allInterfaces.isEmpty())
{
final Class interfaceClass = allInterfaces.iterator().next();
final String implementationClassName = implementationClass.getName();
final String interfaceClassName = interfaceClass.getName();
metafacadeClasses.metafacadesByImpls.put(
implementationClassName,
interfaceClassName);
metafacadeClasses.implsByMetafacades.put(
interfaceClassName,
implementationClassName);
}
}
}
}
}
}
}
}
/**
* Attempts to retrieve the metafacade classes instance with the current active namespace
* and throws an exception if one can not be found.
*
* @return the metafacade classes instance.
*/
private MetafacadeClasses getMetafacadeClasses()
{
final MetafacadeClasses classes = this.metafacadeClasses.get(this.metafacadeModelNamespace);
if (classes == null)
{
throw new MetafacadeImplsException("Namespace '" + this.metafacadeModelNamespace + "' is not a registered metafacade model facade namespace");
}
return classes;
}
/**
* Retrieves the metafacade class from the passed in <code>metafacadeImplClass</code>. Will return a
* MetafacadeImplsException if a metafacade class can not be found for the <code>metafacadeImplClass</code>
*
* @param metafacadeImplClass the name of the metafacade implementation class.
* @return the metafacade Class
*/
public Class getMetafacadeClass(final String metafacadeImplClass)
{
ExceptionUtils.checkEmpty(
"metafacadeImplClass",
metafacadeImplClass);
return this.getMetafacadeClasses().getMetafacadeClass(metafacadeImplClass);
}
/**
* Retrieves the metafacade implementation class from the passed in <code>metafacadeClass</code>. Will return a
* MetafacadeImplsException if a metafacade implementation class can not be found for the
* <code>metafacadeClass</code>
*
* @param metafacadeClass the name of the metafacade class.
* @return the metafacade implementation Class
*/
public Class getMetafacadeImplClass(final String metafacadeClass)
{
ExceptionUtils.checkEmpty(
"metafacadeClass",
metafacadeClass);
return this.getMetafacadeClasses().getMetafacadeImplClass(metafacadeClass);
}
/**
* Stores the metafacade interface and implementation classes.
*/
static final class MetafacadeClasses
{
/**
* Stores all <code>metafacade</code> implementation classes keyed by <code>metafacade</code> interface class.
*/
Map<String, String> implsByMetafacades = new LinkedHashMap<String, String>();
/**
* Stores all <code>metafacade</code> interface classes keyed by <code>metafacade</code> implementation class.
*/
Map<String, String> metafacadesByImpls = new LinkedHashMap<String, String>();
/**
* Retrieves the metafacade class from the passed in <code>metafacadeImplClass</code>. Will return a
* MetafacadeImplsException if a metafacade class can not be found for the <code>metafacadeImplClass</code>
*
* @param metafacadeImplClass the name of the metafacade implementation class.
* @return the metafacade Class
*/
Class getMetafacadeClass(final String metafacadeImplClass)
{
ExceptionUtils.checkEmpty(
"metafacadeImplClass",
metafacadeImplClass);
Class metafacadeClass = null;
try
{
final String metafacadeClassName = this.metafacadesByImpls.get(metafacadeImplClass);
if (StringUtils.isEmpty(metafacadeClassName))
{
throw new MetafacadeImplsException("Can not find a metafacade interface for --> '" +
metafacadeImplClass + "', check your classpath");
}
metafacadeClass = ClassUtils.loadClass(metafacadeClassName);
}
catch (final Throwable throwable)
{
throw new MetafacadeImplsException(throwable);
}
return metafacadeClass;
}
/**
* Retrieves the metafacade implementation class from the passed in <code>metafacadeClass</code>. Will return a
* MetafacadeImplsException if a metafacade implementation class can not be found for the
* <code>metafacadeClass</code>
*
* @param metafacadeClass the name of the metafacade class.
* @return the metafacade implementation Class
*/
Class getMetafacadeImplClass(final String metafacadeClass)
{
ExceptionUtils.checkEmpty(
"metafacadeClass",
metafacadeClass);
Class metafacadeImplementationClass = null;
try
{
final String metafacadeImplementationClassName = this.implsByMetafacades.get(metafacadeClass);
if (StringUtils.isEmpty(metafacadeImplementationClassName))
{
throw new MetafacadeImplsException("Can not find a metafacade implementation class for --> '" +
metafacadeClass + "' check your classpath");
}
metafacadeImplementationClass = ClassUtils.loadClass(metafacadeImplementationClassName);
}
catch (final Throwable throwable)
{
throw new MetafacadeImplsException(throwable);
}
return metafacadeImplementationClass;
}
/**
* Clears each map of any classes it contains.
*/
void clear()
{
this.metafacadesByImpls.clear();
this.implsByMetafacades.clear();
}
/**
* @see Object#toString()
*/
public String toString()
{
return super.toString() + '[' + this.metafacadesByImpls + ']';
}
}
}