
de.tsl2.nano.service.feature.FeatureFactory Maven / Gradle / Ivy
Show all versions of tsl2.nano.serviceaccess Show documentation
/*
* File: $HeadURL$
* Id : $Id$
*
* created by: Thomas Schneider, Thomas Schneider
* created on: Sep 6, 2010
*
* Copyright: (c) Thomas Schneider 2010, all rights reserved
*/
package de.tsl2.nano.service.feature;
import java.lang.reflect.Proxy;
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.logging.Log;
import de.tsl2.nano.core.ManagedException;
import de.tsl2.nano.core.log.LogFactory;
import de.tsl2.nano.serviceaccess.ServiceFactory;
/**
* provides a mechanism to load optional feature-implementations.
* on calling a feature request, an error may be thrown only, inside a full implemented and permitted (permitted to
* user/mandator) feature. if no feature is assigned, the feature-mechanism will be ignored, means, all features are
* available!
*
* use:
* FeatureFactory.instance().getImpl(myFeatureInterface).myFeatureMethod(...)
* if the feature is implemented and the role is assigned, the implementation will be excecuted. you don't need any if
* clause etc.! it is possible to use {@link #isEnabled(Class)} instead to implement the feature in your current class,
* but it is not recommended - it is not reusable and uses need if-clauses.
*
* if the feature is assigned, but no implementation is available, an exception will be only thrown, if configurable
* {@link #mustImplement()} returns true. If {@link #mustImplement()} returns false, the implementation will be done
* through an empty {@link FeatureProxy}.
* see {@link #getImpl(Class)} to know how the implementation will be found.
*
* to initialize the factory for the first time, a call of
* {@link #createInstance(FeatureFactory, ClassLoader, String, String, String, Boolean)} is obligate - but this will be
* done by the ServiceFactory.
*
* all available feature interfaces are found in a specific package. please see {@link #properties} with keys
* {@link #KEY_INTERF_PACKAGE_PREFIX}.
*
* Tip:
* If you only want to ask, if a feature was enabled or activated, call {@link #isEnabled(Class)}.
*
* @author Thomas Schneider, Thomas Schneider
* @version $Revision$
*/
public class FeatureFactory {
protected static final Log LOG = LogFactory.getLog(FeatureFactory.class);
private static FeatureFactory self;
private final ClassLoader classloader;
private final Map, Object> implementations = new Hashtable, Object>();
private final Properties properties;
public static final String KEY_INTERF_PACKAGE_PREFIX = "interface_package_prefix";
public static final String KEY_IMPL_PACKAGE_POSTFIX = "impl_package_postfix";
public static final String KEY_IMPL_CLASS_POSTFIX = "impl_class_postfix";
public static final String KEY_MUST_IMPLEMENT = "must_implement";
/**
* constructor
*
* @param classloader classloader of your plugin
* @param featureInterfacePrefix (optional) package postfix (default: null)
* @param implPackagePrefix (optional) package postfix (default: .impl)
* @param implClassPostfix (optional) class postifx (default: Impl)
* @param mustImplement (optional) if true, the feature interfaces must be implemented (default: true)
*/
private FeatureFactory(ClassLoader classloader,
String featureInterfacePrefix,
String implPackagePostfix,
String implClassPostfix,
Boolean mustImplement) {
super();
this.classloader = classloader;
//IMPROVE: perhaps we load the properties from file
properties = new Properties();
properties.put(KEY_INTERF_PACKAGE_PREFIX, featureInterfacePrefix != null ? featureInterfacePrefix : "");
properties.put(KEY_IMPL_PACKAGE_POSTFIX, implPackagePostfix != null ? implPackagePostfix : ".impl");
properties.put(KEY_IMPL_CLASS_POSTFIX, implClassPostfix != null ? implClassPostfix : "Impl");
properties.put(KEY_MUST_IMPLEMENT, mustImplement != null ? mustImplement.toString() : "false");
}
/**
* must be called before using the singelton.
*
* @param factoryImpl optional own instance of module factory
* @param featureInterfacePrefix (optional) package postfix (default: null)
* @param implPackagePrefix (optional) package postfix (default: .impl)
* @param implClassPostfix (optional) class postifx (default: Impl)
* @param mustImplement (optional) if true, the feature interfaces must be implemented (default: true)
*/
public static final void createInstance(FeatureFactory factoryImpl,
ClassLoader classloader,
String featureInterfacePrefix,
String implPackagePostfix,
String implClassPostfix,
Boolean mustImplement) {
if (factoryImpl == null) {
self = new FeatureFactory(classloader,
featureInterfacePrefix,
implPackagePostfix,
implClassPostfix,
mustImplement);
} else {
self = factoryImpl;
}
LOG.info("FeatureFactory singelton instance assigned: " + self);
}
/**
* instance
*
* @return singelton
*/
public static final FeatureFactory instance() {
assert self != null : "please call createInstance(..) before!";
ServiceFactory.checkConnection();
return self;
}
/**
* returns the feature implementation (packed into a {@link FeatureProxy})
* the (optional) implementation must be found in
* interfaze-package + impl_package_postfix + interfaze-class + impl_class_postfix
*
* example:
*
*
* feature-name: ErweiterteAktenSuche
* ==> interfaze: mypackage.feature.IErweiterteAktenSucheFeature
* ==> impl : mypackage.feature.impl.ErweiterteAktenSucheFeatureImpl
*
*
* if the feature-factory was initialized with {@link #mustImplement()} == true, an exception will be thrown, if no
* implementation was found - otherwise an empty FeatureProxy will be used.
*
* @param interface type
* @param interfaze source interface
* @return implementation object
*/
public T getImpl(Class interfaze) {
return getImpl(interfaze, mustImplement());
}
/**
* see description of {@link #getImpl(Class)}. if mustImplement is true, a {@link ClassNotFoundException} will be
* thrown, if no implementation was found.
*
* @param interface type
* @param interfaze source interface
* @param mustImplement if true, an interfaze implementation must be found
* @return implementation object
*/
protected T getImpl(Class interfaze, boolean mustImplement) {
T impl = (T) implementations.get(interfaze);
if (impl == null) {
checkInterfacePath(interfaze);
final String implClassName = interfaze.getPackage().getName() + properties.getProperty(KEY_IMPL_PACKAGE_POSTFIX)
+ "."
+ interfaze.getSimpleName().substring(1)
+ properties.getProperty(KEY_IMPL_CLASS_POSTFIX);
LOG.info("Loading Feature request for: " + implClassName);
Class clazz;
try {
clazz = (Class) classloader.loadClass(implClassName);
} catch (final ClassNotFoundException e) {
if (mustImplement) {
LOG.error("FeatureFactory couldn't load implementation class " + implClassName);
ManagedException.forward(e);
return null;
} else {
clazz = null;
LOG.warn("No Feature implementation '" + implClassName + "' found ==> using empty FeatureProxy!");
}
}
T instance = null;
if (clazz != null) {
try {
instance = clazz.newInstance();
} catch (final Exception e) {
ManagedException.forward(e);
}
}
final T proxy = FeatureProxy.createBeanImplementation(interfaze, instance, classloader);
implementations.put(interfaze, proxy);
impl = proxy;
}
return impl;
}
/**
* defines, whether a feature implementation must be present
*
* @return true, if must_implement property is true
*/
private boolean mustImplement() {
return Boolean.valueOf(properties.getProperty(KEY_MUST_IMPLEMENT));
}
/**
* delegates to {@link FeatureProxy#isEnabled()}
*
* @param interface type
* @return true, if mandator feature was enabled
*/
public boolean isEnabled(Class interfaze) {
final Proxy proxyObject = (Proxy) getImpl(interfaze, false);
final FeatureProxy impl = (FeatureProxy) (Proxy.getInvocationHandler(proxyObject));
return impl.isEnabled();
}
/**
* checks the given interface package path to start with defined feature interface path.
*
* @param
* @param interfaze interface package to check
*/
protected void checkInterfacePath(Class interfaze) {
String featurePathConstraint = properties.getProperty(KEY_INTERF_PACKAGE_PREFIX);
if (featurePathConstraint != null) {
if (!interfaze.getPackage().getName().startsWith(featurePathConstraint)) {
throw new ManagedException("tsl2nano.implementationerror",
new Object[] { "Feature-Interface: " + interfaze.getName(),
"Please use only feature interfaces in packages starting with '" + featurePathConstraint + "'!" });
}
} else { //not defined? ==> default mechansim
featurePathConstraint = "feature";
if (!interfaze.getPackage().getName().contains(featurePathConstraint)) {
throw new ManagedException("tsl2nano.implementationerror",
new Object[] { "Feature-Interface: " + interfaze.getName(),
"Please use only feature interfaces in packages containing the substring '" + featurePathConstraint
+ "'!" });
}
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return super.toString() + ": " + properties;
}
}