org.milyn.javabean.factory.FactoryDefinitionParser Maven / Gradle / Ivy
/*
* Milyn - Copyright (C) 2006 - 2010
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License (version 2.1) as published by the Free Software
* Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU Lesser General Public License for more details:
* http://www.gnu.org/licenses/lgpl.txt
*/
package org.milyn.javabean.factory;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.milyn.container.ApplicationContext;
import org.milyn.javabean.BeanPopulator;
import org.milyn.javabean.DataDecodeException;
import org.milyn.util.ClassUtil;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* A factory definition string is an expression that instructs how to create a certain object.
* A {@link FactoryDefinitionParser} can parse the factory definition and create a {@link Factory} object which
* can create the object according to the definition.
*
* A {@link FactoryDefinitionParser} must have a public argumentless constructor. The {@link FactoryDefinitionParser}
* must be thread safe. The parse method can be called concurrently. If the {@link FactoryDefinitionParser} is created
* with the {@link FactoryDefinitionParserFactory} then it will be created only once.
*
* @author [email protected]
*
*/
@SuppressWarnings("deprecation")
public interface FactoryDefinitionParser {
/**
* Parses the factory definition string and creates a factory object
* that can create the object according to the definition.
*
* @param factoryDefinition The factory definition
* @return The Factory object that creates the target object according to the definition.
* @throws InvalidFactoryDefinitionException If the factoryDefinition is invalid
* @throws FactoryException If something went wrong while creating the factory
*/
Factory> parse(String factoryDefinition);
public static class FactoryDefinitionParserFactory {
private static Log logger = LogFactory.getLog(FactoryDefinitionParserFactory.class);
public static final String DEFAULT_ALIAS = "default";
private static volatile ConcurrentMap instances = new ConcurrentHashMap();
private static volatile Map> aliasToClassMap;
public static FactoryDefinitionParser getInstance(String alias, ApplicationContext applicationContext) {
String className;
if(StringUtils.isEmpty(alias) || alias.equals(DEFAULT_ALIAS)) {
className = applicationContext.getStore().getGlobalParams().getStringParameter(BeanPopulator.GLOBAL_DEFAULT_FACTORY_DEFINITION_PARSER_CLASS, BeanPopulator.DEFAULT_FACTORY_DEFINITION_PARSER_CLASS);
} else {
loadAliasToClassMap();
Class extends FactoryDefinitionParser> clazz = aliasToClassMap.get(alias);
if(clazz == null) {
//We couldn't find any class that uses that alias so maybe the alias is a class name.
try {
clazz = ClassUtil.forName(alias, FactoryDefinitionParser.class);
className = clazz.getName();
} catch (ClassNotFoundException e) {
throw new IllegalFactoryAliasException("The FactoryDefinitionParser alias '" + alias + "' can't be found and doesn't seem to be a classname.", e);
}
}
className = clazz.getName();
}
FactoryDefinitionParser factoryDefinitionParser = instances.get(className);
if(factoryDefinitionParser == null) {
try {
@SuppressWarnings("unchecked")
Class factoryDefinitionParserClass = ClassUtil.forName(className, FactoryDefinitionParser.class);
FactoryDefinitionParser newFactoryDefinitionParser = factoryDefinitionParserClass.newInstance();
instances.putIfAbsent(className, newFactoryDefinitionParser);
// We do an extra get to make sure that there is always only one factoryDefinitionParser instance
factoryDefinitionParser = instances.get(className);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("The FactoryDefinitionParser class '"+ className +"' can't be found", e);
} catch (InstantiationException e) {
throw new IllegalArgumentException("The FactoryDefinitionParser class '"+ className +"'can't be instantiated. The FactoryDefinitionParser class must have a argumentless public constructor.", e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("The FactoryDefinitionParser class '"+ className +"' can't be instantiated.", e);
}
}
return factoryDefinitionParser;
}
public static FactoryDefinitionParser getInstance(ApplicationContext applicationContext) {
return getInstance("default", applicationContext);
}
public static Map> getAliasToClassMap() {
loadAliasToClassMap();
return Collections.unmodifiableMap(aliasToClassMap);
}
private synchronized static void loadAliasToClassMap() throws DataDecodeException {
if(aliasToClassMap == null) {
synchronized (FactoryDefinitionParserFactory.class) {
if(aliasToClassMap == null) {
List> factories = ClassUtil.getClasses("META-INF/smooks-javabean-factory-definition-parsers.inf", FactoryDefinitionParser.class);
Set toRemove = new HashSet();
aliasToClassMap = new HashMap>();
for (Class extends FactoryDefinitionParser> factory : factories) {
Alias alias = (Alias) factory.getAnnotation(Alias.class);
if(alias != null) {
String[] names = alias.value();
for (String name : names) {
if(name.equals(DEFAULT_ALIAS)) {
throw new IllegalFactoryAliasException("The alias 'default' is a reserved alias name. Please use a different name");
}
if(aliasToClassMap.containsKey(name)) {
Class extends FactoryDefinitionParser> prevClass = aliasToClassMap.get(name);
logger.warn("More than one FactoryDefinitionParser has the alias '" + name + "' on the classpath. Previous: '" + prevClass.getName() + "'. Current '" + factory.getName() + "'. To use one of these factories you will have to declare the complete class name as alias.");
toRemove.add(name); // We register that we need to remove that one. We keep it for to be able to give clear warning messages.
}
aliasToClassMap.put(name, factory);
}
}
}
//We remove all alias that we defined multiple times
for(String name : toRemove) {
aliasToClassMap.remove(name);
}
}
}
}
}
}
}
;