/*
* This code is distributed under The GNU Lesser General Public License (LGPLv3)
* Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
*
* Copyright Denis Pavlov 2009
* Web: http://www.genericdtoassembler.org
* SVN: https://svn.code.sf.net/p/geda-genericdto/code/trunk/
* SVN (mirror): http://geda-genericdto.googlecode.com/svn/trunk/
*/
package com.inspiresoftware.lib.dto.geda.assembler;
import com.inspiresoftware.lib.dto.geda.assembler.annotations.AnnotationProxy;
import com.inspiresoftware.lib.dto.geda.assembler.annotations.impl.AnnotationProxies;
import com.inspiresoftware.lib.dto.geda.assembler.extension.Cache;
import com.inspiresoftware.lib.dto.geda.assembler.extension.MethodSynthesizer;
import com.inspiresoftware.lib.dto.geda.assembler.extension.impl.IntHashTable;
import com.inspiresoftware.lib.dto.geda.assembler.extension.impl.SoftReferenceCache;
import com.inspiresoftware.lib.dto.geda.dsl.Registry;
import com.inspiresoftware.lib.dto.geda.exception.*;
import java.util.Properties;
import java.util.WeakHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Assemble DTO and Entities depending on the annotations of Dto.
*
* @author Denis Pavlov
* @since 1.0.0
*
*/
@SuppressWarnings("unchecked")
public final class DTOAssembler {
/**
* String that defines key for synthesizer implementation to use.
*/
public static final String SETTING_SYNTHESIZER_IMPL =
"com.inspiresoftware.lib.dto.geda.assembler.DTOAssembler.SETTING_SYNTHESIZER_IMPL";
/**
* String that defines key for pattern for blacklisting entity class names.
* E.g. Javasist implementation enhances classes by subclassing and adding _$$_javasist
* prefix. This pattern will allow to detect these classes and look up parent class names
* to overcome class cast exceptions.
*/
public static final String SETTING_ENTITY_CLASS_NAME_BLACKLIST_PATTERN =
"com.inspiresoftware.lib.dto.geda.assembler.DTOAssembler.SETTING_ENTITY_CLASS_NAME_BLACKLIST_PATTERN";
private static final String SETTING_ENTITY_CLASS_NAME_BLACKLIST_PATTERN_DEFAULT = "_\\$\\$_";
private static Pattern entityClassNameBlacklistPatternValue = Pattern.compile(SETTING_ENTITY_CLASS_NAME_BLACKLIST_PATTERN_DEFAULT);
private static final WeakHashMap<ClassLoader, Cache<Assembler>> CL_CACHE = new WeakHashMap<ClassLoader, Cache<Assembler>>();
private static final WeakHashMap<ClassLoader, MethodSynthesizer> CL_SYNTHESIZER = new WeakHashMap<ClassLoader, MethodSynthesizer>();
private static final IntHashTable<Boolean> WHITELIST_ENTITIES = new IntHashTable<Boolean>();
private static final IntHashTable<Class[]> AUTOBINDING = new IntHashTable<Class[]>();
/**
* Setup allows to configure some of the behaviour of GeDA. Currently it is used to tune the caching cleanup cycles.
* There are two caches used in GeDA:
* <ul>
* <li>DTOAssembler cache - that caches the assemblers instances</li>
* <li>Dynamic classes cache - that caches the instances of {@link com.inspiresoftware.lib.dto.geda.assembler.extension.DataReader}s
* and {@link com.inspiresoftware.lib.dto.geda.assembler.extension.DataWriter}s</li>
* </ul>
*
* @param props properties with key specified by DTOAssembler.SETTINGS_* keys
*
* @throws NumberFormatException if the number of cycles specified in properties cannot be converted to int.
* @throws GeDAException if any of the configurations cause exception
*/
public static void setup(final Properties props) throws NumberFormatException, GeDAException {
final String synthesizerImpl = props.getProperty(SETTING_SYNTHESIZER_IMPL);
final String classNameBlacklistPattern = props.getProperty(SETTING_ENTITY_CLASS_NAME_BLACKLIST_PATTERN);
if (synthesizerImpl != null) {
for (final MethodSynthesizer synthesizer : CL_SYNTHESIZER.values()) {
synthesizer.configure("synthesizerImpl", synthesizerImpl);
}
}
if (classNameBlacklistPattern != null) {
entityClassNameBlacklistPatternValue = Pattern.compile(classNameBlacklistPattern);
}
}
private static Class filterBlacklisted(final Class className) {
final int hash = className.hashCode();
if (!WHITELIST_ENTITIES.containsKey(hash)) {
if (matches(className.getSimpleName())) {
if (!className.getSuperclass().equals(Object.class)) {
// some proxies are derived straight from Object.class - we do not want those
return filterBlacklisted(className.getSuperclass());
}
} else {
WHITELIST_ENTITIES.put(hash, Boolean.TRUE);
}
}
return className;
}
/**
* Exposed for testing.
*
* @param className entity class name
* @return true if class name matches pattern, which will result in going one class level up.
*/
static boolean matches(final String className) {
final Matcher match = entityClassNameBlacklistPatternValue.matcher(className);
return match.find();
}
private static Assembler getAssemblerFromCache(final ClassLoader cl, final int cacheKey) {
if (CL_CACHE.containsKey(cl)) {
return CL_CACHE.get(cl).get(cacheKey);
}
return null;
}
private static Assembler putAssemblerToCache(final ClassLoader cl, final int cacheKey, final Assembler asm) {
Cache<Assembler> cache = CL_CACHE.get(cl);
if (cache == null) {
cache = new SoftReferenceCache<Assembler>();
CL_CACHE.put(cl, cache);
}
cache.put(cacheKey, asm);
return asm;
}
private static Assembler createNewAssembler(
final Class< ? > dto,
final Class< ? > entity,
final ClassLoader classLoader,
final Object synthesizer,
final Registry registry)
throws InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
GeDARuntimeException, AnnotationDuplicateBindingException {
if (synthesizer instanceof MethodSynthesizerProxy) {
return new DTOtoEntityAssemblerImpl(dto, entity, classLoader, (MethodSynthesizer) synthesizer, registry);
}
return new DTOtoEntityAssemblerImpl(dto, entity, classLoader, new MethodSynthesizerProxy(classLoader, synthesizer), registry);
}
private static Assembler createNewAssembler(
final Class< ? > dto,
final Class< ? >[] entities,
final ClassLoader classLoader,
final Object synthesizer,
final Registry registry)
throws InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
GeDARuntimeException, AnnotationDuplicateBindingException {
if (synthesizer instanceof MethodSynthesizerProxy) {
return new DTOtoEntitiesAssemblerDecoratorImpl(dto, entities, classLoader, (MethodSynthesizer) synthesizer, registry);
}
return new DTOtoEntitiesAssemblerDecoratorImpl(dto, entities, classLoader, new MethodSynthesizerProxy(classLoader, synthesizer), registry);
}
private static AnnotationProxy getDtoAnnotation(final Class<?> dto) {
final AnnotationProxy ann = AnnotationProxies.getClassAnnotationProxy(dto);
if (ann.annotationExists()) {
return ann;
}
throw new AnnotationMissingException(dto.getCanonicalName());
}
private static AnnotationProxy getDtoAnnotationAuto(final Class<?> dto)
throws AnnotationMissingException, AnnotationMissingAutobindingException {
final AnnotationProxy ann = AnnotationProxies.getClassAnnotationProxy(dto);
if (!ann.annotationExists()) {
throw new AnnotationMissingException(dto.getCanonicalName());
}
final String[] auto = ann.getValue("value");
if (auto == null || auto.length == 0) {
throw new AnnotationMissingAutobindingException(dto.getCanonicalName());
}
return ann;
}
private static Class[] detectAutobinding(final Class< ? > dto)
throws AutobindingClassNotFoundException {
final int cacheKey = dto.hashCode();
if (AUTOBINDING.containsKey(cacheKey)) {
return AUTOBINDING.get(cacheKey);
}
final AnnotationProxy ann = getDtoAnnotationAuto(dto);
final String[] auto = ann.getValue("value");
final Class[] classes = new Class[auto.length];
for (int i = 0; i < auto.length; i++) {
final String clazz = auto[i];
try {
if (clazz == null || clazz.length() == 0) {
throw new AnnotationMissingAutobindingException(dto.getCanonicalName());
}
classes[i] = Class.forName(clazz, true, dto.getClassLoader());
} catch (ClassNotFoundException cnfe) {
throw new AutobindingClassNotFoundException(dto.getCanonicalName(), clazz);
}
}
AUTOBINDING.put(cacheKey, classes);
return classes;
}
private static MethodSynthesizer getDefaultSynthesizer(final ClassLoader classLoader) {
MethodSynthesizer syn = CL_SYNTHESIZER.get(classLoader);
if (syn == null) {
syn = new MethodSynthesizerProxy(classLoader);
CL_SYNTHESIZER.put(classLoader, syn);
}
return syn;
}
private static <DTO, Entity> int createAssemblerKey(
final Class<DTO> dto,
final Class<Entity> entity,
final Object synthesizer,
final Registry registry) {
int result = dto.hashCode();
result = 31 * result + entity.hashCode();
result = 31 * result + synthesizer.hashCode();
if (registry != null) {
result = 31 * result + registry.hashCode();
}
return result;
}
private static <DTO, Entity> int createAssemblerKey(
final Class<DTO> dto,
final Class<Entity>[] entities,
final Object synthesizer,
final Registry registry) {
int result = dto.hashCode();
for (final Class entity : entities) {
result = 31 * result + entity.hashCode();
}
result = 31 * result + synthesizer.hashCode();
if (registry != null) {
result = 31 * result + registry.hashCode();
}
return result;
}
/**
* @param dto Dto concrete class that is annotated.
* @param entity the entity class or interface that has appropriate getters and setters
* @param synthesizer custom method synthesizer to use (see {@link com.inspiresoftware.lib.dto.geda.assembler.MethodSynthesizerProxy} )
* @return assembler instance for this conversion.
*
* @throws AnnotationMissingException if dto class is missing the {@link com.inspiresoftware.lib.dto.geda.annotations.Dto} annotation.
* @throws InspectionInvalidDtoInstanceException if dto instance used for read/write operation is not valid
* @throws InspectionInvalidEntityInstanceException if entity instance used for read/write operation is not valid
* @throws AnnotationDuplicateBindingException if during mapping scan same dto field is mapped more than once
* @throws GeDARuntimeException unhandled cases - this is (if GeDA was not tampered with) means library failure and should be reported
* @throws AnnotationValidatingBindingException in case binding create has a mismatching return type/parameters
* @throws AnnotationMissingBindingException in case when no valid property on entity is specified to bind to
* @throws InspectionBindingNotFoundException in case when no valid property on entity is found to bind to
* @throws InspectionPropertyNotFoundException in case a binding field cannot be found
* @throws UnableToCreateInstanceException if an instance of an auto created class (one that is directly created by GeDA) cannot be instantiated
* @throws InspectionScanningException general error that may occur during scanning a class for fields and method descriptors
*/
public static Assembler newCustomAssembler(
final Class< ? > dto,
final Class< ? > entity,
final Object synthesizer)
throws AnnotationMissingException, InspectionInvalidDtoInstanceException, InspectionInvalidEntityInstanceException,
InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
GeDARuntimeException, AnnotationDuplicateBindingException {
return newCustomAssembler(dto, entity, DTOAssembler.class.getClassLoader(), synthesizer);
}
/**
* @param dto Dto concrete class that is annotated.
* @param entity the entity class or interface that has appropriate getters and setters
* @param classLoader class loader for object graph serviced by this assembler
* @param synthesizer custom method synthesizer to use (see {@link com.inspiresoftware.lib.dto.geda.assembler.MethodSynthesizerProxy} )
* @return assembler instance for this conversion.
*
* @throws AnnotationMissingException if dto class is missing the {@link com.inspiresoftware.lib.dto.geda.annotations.Dto} annotation.
* @throws InspectionInvalidDtoInstanceException if dto instance used for read/write operation is not valid
* @throws InspectionInvalidEntityInstanceException if entity instance used for read/write operation is not valid
* @throws AnnotationDuplicateBindingException if during mapping scan same dto field is mapped more than once
* @throws GeDARuntimeException unhandled cases - this is (if GeDA was not tampered with) means library failure and should be reported
* @throws AnnotationValidatingBindingException in case binding create has a mismatching return type/parameters
* @throws AnnotationMissingBindingException in case when no valid property on entity is specified to bind to
* @throws InspectionBindingNotFoundException in case when no valid property on entity is found to bind to
* @throws InspectionPropertyNotFoundException in case a binding field cannot be found
* @throws UnableToCreateInstanceException if an instance of an auto created class (one that is directly created by GeDA) cannot be instantiated
* @throws InspectionScanningException general error that may occur during scanning a class for fields and method descriptors
*/
public static Assembler newCustomAssembler(
final Class< ? > dto,
final Class< ? > entity,
final ClassLoader classLoader,
final Object synthesizer)
throws AnnotationMissingException, InspectionInvalidDtoInstanceException, InspectionInvalidEntityInstanceException,
InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
GeDARuntimeException, AnnotationDuplicateBindingException {
final Class< ? > realEntity = filterBlacklisted(entity);
final int key = createAssemblerKey(dto, realEntity, synthesizer, null);
final Assembler asm = getAssemblerFromCache(classLoader, key);
if (asm != null) {
return asm;
}
getDtoAnnotation(dto);
return putAssemblerToCache(classLoader, key, createNewAssembler(dto, realEntity, classLoader, synthesizer, null));
}
/**
* @param dto Dto concrete class that is annotated.
* @param entity the entity class or interface that has appropriate getters and setters
* @param registry DSL registry that contains all mappings
* @param synthesizer custom method synthesizer to use (see {@link com.inspiresoftware.lib.dto.geda.assembler.MethodSynthesizerProxy} )
* @return assembler instance for this conversion.
*
* @throws InspectionInvalidDtoInstanceException if dto instance used for read/write operation is not valid
* @throws InspectionInvalidEntityInstanceException if entity instance used for read/write operation is not valid
* @throws AnnotationDuplicateBindingException if during mapping scan same dto field is mapped more than once
* @throws GeDARuntimeException unhandled cases - this is (if GeDA was not tampered with) means library failure and should be reported
* @throws AnnotationValidatingBindingException in case binding create has a mismatching return type/parameters
* @throws AnnotationMissingBindingException in case when no valid property on entity is specified to bind to
* @throws InspectionBindingNotFoundException in case when no valid property on entity is found to bind to
* @throws InspectionPropertyNotFoundException in case a binding field cannot be found
* @throws UnableToCreateInstanceException if an instance of an auto created class (one that is directly created by GeDA) cannot be instantiated
* @throws InspectionScanningException general error that may occur during scanning a class for fields and method descriptors
*/
public static Assembler newCustomAssembler(
final Class< ? > dto,
final Class< ? > entity,
final Registry registry,
final Object synthesizer)
throws AnnotationMissingException, InspectionInvalidDtoInstanceException, InspectionInvalidEntityInstanceException,
InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
GeDARuntimeException, AnnotationDuplicateBindingException {
return newCustomAssembler(dto, entity, DTOAssembler.class.getClassLoader(), registry, synthesizer);
}
/**
* @param dto Dto concrete class that is annotated.
* @param entity the entity class or interface that has appropriate getters and setters
* @param classLoader class loader for object graph serviced by this assembler
* @param registry DSL registry that contains all mappings
* @param synthesizer custom method synthesizer to use (see {@link com.inspiresoftware.lib.dto.geda.assembler.MethodSynthesizerProxy} )
* @return assembler instance for this conversion.
*
* @throws InspectionInvalidDtoInstanceException if dto instance used for read/write operation is not valid
* @throws InspectionInvalidEntityInstanceException if entity instance used for read/write operation is not valid
* @throws AnnotationDuplicateBindingException if during mapping scan same dto field is mapped more than once
* @throws GeDARuntimeException unhandled cases - this is (if GeDA was not tampered with) means library failure and should be reported
* @throws AnnotationValidatingBindingException in case binding create has a mismatching return type/parameters
* @throws AnnotationMissingBindingException in case when no valid property on entity is specified to bind to
* @throws InspectionBindingNotFoundException in case when no valid property on entity is found to bind to
* @throws InspectionPropertyNotFoundException in case a binding field cannot be found
* @throws UnableToCreateInstanceException if an instance of an auto created class (one that is directly created by GeDA) cannot be instantiated
* @throws InspectionScanningException general error that may occur during scanning a class for fields and method descriptors
*/
public static Assembler newCustomAssembler(
final Class< ? > dto,
final Class< ? > entity,
final ClassLoader classLoader,
final Registry registry,
final Object synthesizer)
throws AnnotationMissingException, InspectionInvalidDtoInstanceException, InspectionInvalidEntityInstanceException,
InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
GeDARuntimeException, AnnotationDuplicateBindingException {
final Class< ? > realEntity = filterBlacklisted(entity);
final int key = createAssemblerKey(dto, realEntity, synthesizer, registry);
final Assembler asm = getAssemblerFromCache(classLoader, key);
if (asm != null) {
return asm;
}
if (registry == null) {
throw new GeDARuntimeException("Registry cannot be null");
}
return putAssemblerToCache(classLoader, key, createNewAssembler(dto, realEntity, classLoader, synthesizer, registry));
}
/**
* @param dto Dto concrete class that is annotated.
* @param entities the entity classes or interfaces that have appropriate getters and setters
* @param synthesizer custom method synthesizer to use (see {@link com.inspiresoftware.lib.dto.geda.assembler.MethodSynthesizerProxy} )
* @return assembler instance for this conversion.
*
* @throws AnnotationMissingException if dto class is missing the {@link com.inspiresoftware.lib.dto.geda.annotations.Dto} annotation.
* @throws InspectionInvalidDtoInstanceException if dto instance used for read/write operation is not valid
* @throws InspectionInvalidEntityInstanceException if entity instance used for read/write operation is not valid
* @throws AnnotationDuplicateBindingException if during mapping scan same dto field is mapped more than once
* @throws GeDARuntimeException unhandled cases - this is (if GeDA was not tampered with) means library failure and should be reported
* @throws AnnotationValidatingBindingException in case binding create has a mismatching return type/parameters
* @throws AnnotationMissingBindingException in case when no valid property on entity is specified to bind to
* @throws InspectionBindingNotFoundException in case when no valid property on entity is found to bind to
* @throws InspectionPropertyNotFoundException in case a binding field cannot be found
* @throws UnableToCreateInstanceException if an instance of an auto created class (one that is directly created by GeDA) cannot be instantiated
* @throws InspectionScanningException general error that may occur during scanning a class for fields and method descriptors
*/
public static Assembler newCustomCompositeAssembler(
final Class< ? > dto,
final Class< ? >[] entities,
final Object synthesizer)
throws AnnotationMissingException, InspectionInvalidDtoInstanceException, InspectionInvalidEntityInstanceException,
InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
GeDARuntimeException, AnnotationDuplicateBindingException {
return newCustomCompositeAssembler(dto, entities, DTOAssembler.class.getClassLoader(), synthesizer);
}
/**
* @param dto Dto concrete class that is annotated.
* @param entities the entity classes or interfaces that have appropriate getters and setters
* @param classLoader class loader for object graph serviced by this assembler
* @param synthesizer custom method synthesizer to use (see {@link com.inspiresoftware.lib.dto.geda.assembler.MethodSynthesizerProxy} )
* @return assembler instance for this conversion.
*
* @throws AnnotationMissingException if dto class is missing the {@link com.inspiresoftware.lib.dto.geda.annotations.Dto} annotation.
* @throws InspectionInvalidDtoInstanceException if dto instance used for read/write operation is not valid
* @throws InspectionInvalidEntityInstanceException if entity instance used for read/write operation is not valid
* @throws AnnotationDuplicateBindingException if during mapping scan same dto field is mapped more than once
* @throws GeDARuntimeException unhandled cases - this is (if GeDA was not tampered with) means library failure and should be reported
* @throws AnnotationValidatingBindingException in case binding create has a mismatching return type/parameters
* @throws AnnotationMissingBindingException in case when no valid property on entity is specified to bind to
* @throws InspectionBindingNotFoundException in case when no valid property on entity is found to bind to
* @throws InspectionPropertyNotFoundException in case a binding field cannot be found
* @throws UnableToCreateInstanceException if an instance of an auto created class (one that is directly created by GeDA) cannot be instantiated
* @throws InspectionScanningException general error that may occur during scanning a class for fields and method descriptors
*/
public static Assembler newCustomCompositeAssembler(
final Class< ? > dto,
final Class< ? >[] entities,
final ClassLoader classLoader,
final Object synthesizer)
throws AnnotationMissingException, InspectionInvalidDtoInstanceException, InspectionInvalidEntityInstanceException,
InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
GeDARuntimeException, AnnotationDuplicateBindingException {
getDtoAnnotation(dto);
return createNewAssembler(dto, entities, classLoader, synthesizer, null);
}
/**
* @param dto Dto concrete class that is annotated.
* @param entity the entity class or interface that has appropriate getters and setters
* @return assembler instance for this conversion.
*
* @throws AnnotationMissingException if dto class is missing the {@link com.inspiresoftware.lib.dto.geda.annotations.Dto} annotation.
* @throws InspectionInvalidDtoInstanceException if dto instance used for read/write operation is not valid
* @throws InspectionInvalidEntityInstanceException if entity instance used for read/write operation is not valid
* @throws AnnotationDuplicateBindingException if during mapping scan same dto field is mapped more than once
* @throws GeDARuntimeException unhandled cases - this is (if GeDA was not tampered with) means library failure and should be reported
* @throws AnnotationValidatingBindingException in case binding create has a mismatching return type/parameters
* @throws AnnotationMissingBindingException in case when no valid property on entity is specified to bind to
* @throws InspectionBindingNotFoundException in case when no valid property on entity is found to bind to
* @throws InspectionPropertyNotFoundException in case a binding field cannot be found
* @throws UnableToCreateInstanceException if an instance of an auto created class (one that is directly created by GeDA) cannot be instantiated
* @throws InspectionScanningException general error that may occur during scanning a class for fields and method descriptors
*/
public static Assembler newAssembler(
final Class< ? > dto,
final Class< ? > entity)
throws AnnotationMissingException, InspectionInvalidDtoInstanceException, InspectionInvalidEntityInstanceException,
InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
GeDARuntimeException, AnnotationDuplicateBindingException {
return newAssembler(dto, entity, DTOAssembler.class.getClassLoader());
}
/**
* @param dto Dto concrete class that is annotated.
* @param entity the entity class or interface that has appropriate getters and setters
* @param classLoader class loader for object graph serviced by this assembler
* @return assembler instance for this conversion.
*
* @throws AnnotationMissingException if dto class is missing the {@link com.inspiresoftware.lib.dto.geda.annotations.Dto} annotation.
* @throws InspectionInvalidDtoInstanceException if dto instance used for read/write operation is not valid
* @throws InspectionInvalidEntityInstanceException if entity instance used for read/write operation is not valid
* @throws AnnotationDuplicateBindingException if during mapping scan same dto field is mapped more than once
* @throws GeDARuntimeException unhandled cases - this is (if GeDA was not tampered with) means library failure and should be reported
* @throws AnnotationValidatingBindingException in case binding create has a mismatching return type/parameters
* @throws AnnotationMissingBindingException in case when no valid property on entity is specified to bind to
* @throws InspectionBindingNotFoundException in case when no valid property on entity is found to bind to
* @throws InspectionPropertyNotFoundException in case a binding field cannot be found
* @throws UnableToCreateInstanceException if an instance of an auto created class (one that is directly created by GeDA) cannot be instantiated
* @throws InspectionScanningException general error that may occur during scanning a class for fields and method descriptors
*/
public static Assembler newAssembler(
final Class< ? > dto,
final Class< ? > entity,
final ClassLoader classLoader)
throws AnnotationMissingException, InspectionInvalidDtoInstanceException, InspectionInvalidEntityInstanceException,
InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
GeDARuntimeException, AnnotationDuplicateBindingException {
final Class< ? > realEntity = filterBlacklisted(entity);
final MethodSynthesizer synthesizer = getDefaultSynthesizer(classLoader);
final int key = createAssemblerKey(dto, realEntity, synthesizer, null);
final Assembler asm = getAssemblerFromCache(classLoader, key);
if (asm != null) {
return asm;
}
getDtoAnnotation(dto);
return putAssemblerToCache(classLoader, key, createNewAssembler(dto, realEntity, classLoader, synthesizer, null));
}
/**
* @param dto Dto concrete class that is annotated.
* @param entity the entity class or interface that has appropriate getters and setters
* @param registry DSL registry that contains all mappings
* @return assembler instance for this conversion.
*
* @throws InspectionInvalidDtoInstanceException if dto instance used for read/write operation is not valid
* @throws InspectionInvalidEntityInstanceException if entity instance used for read/write operation is not valid
* @throws AnnotationDuplicateBindingException if during mapping scan same dto field is mapped more than once
* @throws GeDARuntimeException unhandled cases - this is (if GeDA was not tampered with) means library failure and should be reported
* @throws AnnotationValidatingBindingException in case binding create has a mismatching return type/parameters
* @throws AnnotationMissingBindingException in case when no valid property on entity is specified to bind to
* @throws InspectionBindingNotFoundException in case when no valid property on entity is found to bind to
* @throws InspectionPropertyNotFoundException in case a binding field cannot be found
* @throws UnableToCreateInstanceException if an instance of an auto created class (one that is directly created by GeDA) cannot be instantiated
* @throws InspectionScanningException general error that may occur during scanning a class for fields and method descriptors
*/
public static Assembler newAssembler(
final Class< ? > dto,
final Class< ? > entity,
final Registry registry)
throws InspectionInvalidDtoInstanceException, InspectionInvalidEntityInstanceException,
InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
GeDARuntimeException, AnnotationDuplicateBindingException {
return newAssembler(dto, entity, DTOAssembler.class.getClassLoader(), registry);
}
/**
* @param dto Dto concrete class that is annotated.
* @param entity the entity class or interface that has appropriate getters and setters
* @param classLoader class loader for object graph serviced by this assembler
* @param registry DSL registry that contains all mappings
* @return assembler instance for this conversion.
*
* @throws InspectionInvalidDtoInstanceException if dto instance used for read/write operation is not valid
* @throws InspectionInvalidEntityInstanceException if entity instance used for read/write operation is not valid
* @throws AnnotationDuplicateBindingException if during mapping scan same dto field is mapped more than once
* @throws GeDARuntimeException unhandled cases - this is (if GeDA was not tampered with) means library failure and should be reported
* @throws AnnotationValidatingBindingException in case binding create has a mismatching return type/parameters
* @throws AnnotationMissingBindingException in case when no valid property on entity is specified to bind to
* @throws InspectionBindingNotFoundException in case when no valid property on entity is found to bind to
* @throws InspectionPropertyNotFoundException in case a binding field cannot be found
* @throws UnableToCreateInstanceException if an instance of an auto created class (one that is directly created by GeDA) cannot be instantiated
* @throws InspectionScanningException general error that may occur during scanning a class for fields and method descriptors
*/
public static Assembler newAssembler(
final Class< ? > dto,
final Class< ? > entity,
final ClassLoader classLoader,
final Registry registry)
throws InspectionInvalidDtoInstanceException, InspectionInvalidEntityInstanceException,
InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
GeDARuntimeException, AnnotationDuplicateBindingException {
final Class< ? > realEntity = filterBlacklisted(entity);
final MethodSynthesizer synthesizer = getDefaultSynthesizer(classLoader);
final int key = createAssemblerKey(dto, realEntity, synthesizer, registry);
final Assembler asm = getAssemblerFromCache(classLoader, key);
if (asm != null) {
return asm;
}
if (registry == null) {
throw new GeDARuntimeException("Registry cannot be null");
}
return putAssemblerToCache(classLoader, key, createNewAssembler(dto, realEntity, classLoader, synthesizer, registry));
}
/**
* @param dto Dto concrete class that is annotated.
* @param entities the entity classes or interfaces that has appropriate getters and setters
* @return assembler instance for this conversion.
*
* @throws AnnotationMissingException if dto class is missing the {@link com.inspiresoftware.lib.dto.geda.annotations.Dto} annotation.
* @throws InspectionInvalidDtoInstanceException if dto instance used for read/write operation is not valid
* @throws InspectionInvalidEntityInstanceException if entity instance used for read/write operation is not valid
* @throws AnnotationDuplicateBindingException if during mapping scan same dto field is mapped more than once
* @throws GeDARuntimeException unhandled cases - this is (if GeDA was not tampered with) means library failure and should be reported
* @throws AnnotationValidatingBindingException in case binding create has a mismatching return type/parameters
* @throws AnnotationMissingBindingException in case when no valid property on entity is specified to bind to
* @throws InspectionBindingNotFoundException in case when no valid property on entity is found to bind to
* @throws InspectionPropertyNotFoundException in case a binding field cannot be found
* @throws UnableToCreateInstanceException if an instance of an auto created class (one that is directly created by GeDA) cannot be instantiated
* @throws InspectionScanningException general error that may occur during scanning a class for fields and method descriptors
*/
public static Assembler newCompositeAssembler(
final Class< ? > dto,
final Class< ? >[] entities)
throws AnnotationMissingException, InspectionInvalidDtoInstanceException, InspectionInvalidEntityInstanceException,
InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
GeDARuntimeException, AnnotationDuplicateBindingException {
return newCompositeAssembler(dto, entities, DTOAssembler.class.getClassLoader());
}
/**
* @param dto Dto concrete class that is annotated.
* @param entities the entity classes or interfaces that has appropriate getters and setters
* @param classLoader class loader for object graph serviced by this assembler
* @return assembler instance for this conversion.
*
* @throws AnnotationMissingException if dto class is missing the {@link com.inspiresoftware.lib.dto.geda.annotations.Dto} annotation.
* @throws InspectionInvalidDtoInstanceException if dto instance used for read/write operation is not valid
* @throws InspectionInvalidEntityInstanceException if entity instance used for read/write operation is not valid
* @throws AnnotationDuplicateBindingException if during mapping scan same dto field is mapped more than once
* @throws GeDARuntimeException unhandled cases - this is (if GeDA was not tampered with) means library failure and should be reported
* @throws AnnotationValidatingBindingException in case binding create has a mismatching return type/parameters
* @throws AnnotationMissingBindingException in case when no valid property on entity is specified to bind to
* @throws InspectionBindingNotFoundException in case when no valid property on entity is found to bind to
* @throws InspectionPropertyNotFoundException in case a binding field cannot be found
* @throws UnableToCreateInstanceException if an instance of an auto created class (one that is directly created by GeDA) cannot be instantiated
* @throws InspectionScanningException general error that may occur during scanning a class for fields and method descriptors
*/
public static Assembler newCompositeAssembler(
final Class< ? > dto,
final Class< ? >[] entities,
final ClassLoader classLoader)
throws AnnotationMissingException, InspectionInvalidDtoInstanceException, InspectionInvalidEntityInstanceException,
InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
GeDARuntimeException, AnnotationDuplicateBindingException {
final MethodSynthesizer synthesizer = getDefaultSynthesizer(classLoader);
final int key = createAssemblerKey(dto, entities, synthesizer, null);
final Assembler asm = getAssemblerFromCache(classLoader, key);
if (asm != null) {
return asm;
}
getDtoAnnotation(dto);
return putAssemblerToCache(classLoader, key, createNewAssembler(dto, entities, classLoader, synthesizer, null));
}
/**
* @param dto Dto concrete class that is annotated and value attribute of Dto is supplied.
* @param synthesizer custom method synthesizer to use (see {@link com.inspiresoftware.lib.dto.geda.assembler.MethodSynthesizerProxy} )
* @return assembler instance for this conversion.
*
* @throws AnnotationMissingAutobindingException if autobinding parameter is missing
* @throws AutobindingClassNotFoundException if class specified for auto binding cannot be found
* @throws AnnotationDuplicateBindingException if same field name is mapped more than once
* @throws GeDARuntimeException unhandled cases - this is (if GeDA was not tampered with) means library failure and should be reported
* @throws AnnotationValidatingBindingException in case binding create has a mismatching return type/parameters
* @throws AnnotationMissingBindingException in case when no valid property on entity is specified to bind to
* @throws InspectionBindingNotFoundException in case when no valid property on entity is found to bind to
* @throws InspectionPropertyNotFoundException in case a binding field cannot be found
* @throws UnableToCreateInstanceException if an instance of an auto created class (one that is directly created by GeDA) cannot be instantiated
* @throws InspectionScanningException general error that may occur during scanning a class for fields and method descriptors
*/
public static Assembler newCustomAssembler(
final Class< ? > dto,
final Object synthesizer)
throws AnnotationMissingAutobindingException, AutobindingClassNotFoundException, InspectionScanningException,
UnableToCreateInstanceException, InspectionPropertyNotFoundException, InspectionBindingNotFoundException,
AnnotationMissingBindingException, AnnotationValidatingBindingException, GeDARuntimeException,
AnnotationDuplicateBindingException {
return newCustomAssembler(dto, DTOAssembler.class.getClassLoader(), synthesizer);
}
/**
* @param dto Dto concrete class that is annotated and value attribute of Dto is supplied.
* @param classLoader class loader for object graph serviced by this assembler
* @param synthesizer custom method synthesizer to use (see {@link com.inspiresoftware.lib.dto.geda.assembler.MethodSynthesizerProxy} )
* @return assembler instance for this conversion.
*
* @throws AnnotationMissingAutobindingException if autobinding parameter is missing
* @throws AutobindingClassNotFoundException if class specified for auto binding cannot be found
* @throws AnnotationDuplicateBindingException if same field name is mapped more than once
* @throws GeDARuntimeException unhandled cases - this is (if GeDA was not tampered with) means library failure and should be reported
* @throws AnnotationValidatingBindingException in case binding create has a mismatching return type/parameters
* @throws AnnotationMissingBindingException in case when no valid property on entity is specified to bind to
* @throws InspectionBindingNotFoundException in case when no valid property on entity is found to bind to
* @throws InspectionPropertyNotFoundException in case a binding field cannot be found
* @throws UnableToCreateInstanceException if an instance of an auto created class (one that is directly created by GeDA) cannot be instantiated
* @throws InspectionScanningException general error that may occur during scanning a class for fields and method descriptors
*/
public static Assembler newCustomAssembler(
final Class< ? > dto,
final ClassLoader classLoader,
final Object synthesizer)
throws AnnotationMissingAutobindingException, AutobindingClassNotFoundException, InspectionScanningException,
UnableToCreateInstanceException, InspectionPropertyNotFoundException, InspectionBindingNotFoundException,
AnnotationMissingBindingException, AnnotationValidatingBindingException, GeDARuntimeException,
AnnotationDuplicateBindingException {
final Class[] classes = detectAutobinding(dto);
final int key = createAssemblerKey(dto, classes, synthesizer, null);
final Assembler asm = getAssemblerFromCache(classLoader, key);
if (asm != null) {
return asm;
}
if (classes.length == 1) {
return putAssemblerToCache(classLoader, key, createNewAssembler(dto, classes[0], classLoader, synthesizer, null));
}
return putAssemblerToCache(classLoader, key, createNewAssembler(dto, classes, classLoader, synthesizer, null));
}
/**
* @param dto Dto concrete class that is annotated and value attribute of Dto is supplied.
* @return assembler instance for this conversion.
*
* @throws AnnotationMissingAutobindingException if autobinding parameter is missing
* @throws AutobindingClassNotFoundException if class specified for auto binding cannot be found
* @throws AnnotationDuplicateBindingException if same field name is mapped more than once
* @throws GeDARuntimeException unhandled cases - this is (if GeDA was not tampered with) means library failure and should be reported
* @throws AnnotationValidatingBindingException in case binding create has a mismatching return type/parameters
* @throws AnnotationMissingBindingException in case when no valid property on entity is specified to bind to
* @throws InspectionBindingNotFoundException in case when no valid property on entity is found to bind to
* @throws InspectionPropertyNotFoundException in case a binding field cannot be found
* @throws UnableToCreateInstanceException if an instance of an auto created class (one that is directly created by GeDA) cannot be instantiated
* @throws InspectionScanningException general error that may occur during scanning a class for fields and method descriptors
*/
public static Assembler newAssembler(
final Class< ? > dto)
throws AnnotationMissingAutobindingException, AutobindingClassNotFoundException, InspectionScanningException,
UnableToCreateInstanceException, InspectionPropertyNotFoundException, InspectionBindingNotFoundException,
AnnotationMissingBindingException, AnnotationValidatingBindingException, GeDARuntimeException,
AnnotationDuplicateBindingException {
return newAssembler(dto, DTOAssembler.class.getClassLoader());
}
/**
* @param dto Dto concrete class that is annotated and value attribute of Dto is supplied.
* @param classLoader class loader for object graph serviced by this assembler
* @return assembler instance for this conversion.
*
* @throws AnnotationMissingAutobindingException if autobinding parameter is missing
* @throws AutobindingClassNotFoundException if class specified for auto binding cannot be found
* @throws AnnotationDuplicateBindingException if same field name is mapped more than once
* @throws GeDARuntimeException unhandled cases - this is (if GeDA was not tampered with) means library failure and should be reported
* @throws AnnotationValidatingBindingException in case binding create has a mismatching return type/parameters
* @throws AnnotationMissingBindingException in case when no valid property on entity is specified to bind to
* @throws InspectionBindingNotFoundException in case when no valid property on entity is found to bind to
* @throws InspectionPropertyNotFoundException in case a binding field cannot be found
* @throws UnableToCreateInstanceException if an instance of an auto created class (one that is directly created by GeDA) cannot be instantiated
* @throws InspectionScanningException general error that may occur during scanning a class for fields and method descriptors
*/
public static Assembler newAssembler(
final Class< ? > dto,
final ClassLoader classLoader)
throws AnnotationMissingAutobindingException, AutobindingClassNotFoundException, InspectionScanningException,
UnableToCreateInstanceException, InspectionPropertyNotFoundException, InspectionBindingNotFoundException,
AnnotationMissingBindingException, AnnotationValidatingBindingException, GeDARuntimeException,
AnnotationDuplicateBindingException {
final Class[] classes = detectAutobinding(dto);
final MethodSynthesizer synthesizer = getDefaultSynthesizer(classLoader);
final int key = createAssemblerKey(dto, classes, synthesizer, null);
final Assembler asm = getAssemblerFromCache(classLoader, key);
if (asm != null) {
return asm;
}
if (classes.length == 1) {
return putAssemblerToCache(classLoader, key, createNewAssembler(dto, classes[0], classLoader, synthesizer, null));
}
return putAssemblerToCache(classLoader, key, createNewAssembler(dto, classes, classLoader, synthesizer, null));
}
/**
* Dispose of all GeDA caches and references for given class loader.
* This is useful when you wish to unload a particular module of your
* application and ensure no memory leaks occur. An example use would be
* undeploying an OSGi bundle.
*
* @param classLoader class loader to clean references for
*/
public static void disposeOfDtoAssemblersBy(final ClassLoader classLoader) {
if (classLoader != null) {
final Cache<Assembler> cache = CL_CACHE.get(classLoader);
if (cache != null) {
cache.releaseResources();
CL_CACHE.remove(classLoader);
}
}
}
}