org.openrdf.repository.object.composition.ClassResolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of alibaba-composition-object Show documentation
Show all versions of alibaba-composition-object Show documentation
The Object Composition library merges multiple Java objects into a single multi-subject object.
The newest version!
/*
* Copyright (c) 2009, James Leigh All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the openrdf.org nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
package org.openrdf.repository.object.composition;
import org.openrdf.model.URI;
import org.openrdf.repository.object.composition.helpers.BehaviourConstructor;
import org.openrdf.repository.object.composition.helpers.BehaviourProviderService;
import org.openrdf.repository.object.composition.helpers.ClassComposer;
import org.openrdf.repository.object.exceptions.ObjectCompositionException;
import org.openrdf.repository.object.exceptions.ObjectStoreConfigException;
import org.openrdf.repository.object.managers.PropertyMapper;
import org.openrdf.repository.object.managers.RoleMapper;
import org.openrdf.repository.object.managers.helpers.DirUtil;
import org.openrdf.repository.object.managers.helpers.RoleClassLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static java.lang.reflect.Modifier.isAbstract;
/**
* Find a proxy class that can be used for a set of rdf:types.
*
* @author James Leigh
*
*/
public class ClassResolver {
private static final Set EMPTY_SET = Collections.emptySet();
private static final String PKG_PREFIX = "object.proxies._";
private static final String CLASS_PREFIX = "_EntityProxy";
private static RoleMapper newRoleMapper(ClassLoader cl) throws ObjectStoreConfigException {
if (cl == null) {
return newRoleMapper(ClassResolver.class.getClassLoader());
} else {
RoleMapper mapper = new RoleMapper();
new RoleClassLoader(mapper).loadRoles(cl);
return mapper;
}
}
private final Logger logger = LoggerFactory.getLogger(ClassResolver.class);
private final PropertyMapper properties;
private final ClassFactory cp;
private final Collection> baseClassRoles;
private final RoleMapper mapper;
private final Class> blank;
private final ConcurrentMap, Class>> multiples = new ConcurrentHashMap, Class>>();
private final BehaviourProviderService behaviourService;
public ClassResolver() throws ObjectStoreConfigException {
this(Thread.currentThread().getContextClassLoader());
}
public ClassResolver(ClassLoader cl) throws ObjectStoreConfigException {
this(newRoleMapper(cl), cl == null ? ClassResolver.class.getClassLoader() : cl);
}
public ClassResolver(RoleMapper mapper, ClassLoader cl)
throws ObjectStoreConfigException {
this(mapper, new PropertyMapper(cl, mapper.isNamedTypePresent()), cl);
}
public ClassResolver(RoleMapper mapper, PropertyMapper properties,
ClassLoader cl) throws ObjectStoreConfigException {
this.mapper = mapper;
this.properties = properties;
try {
File dir = DirUtil.createTempDir("classes");
DirUtil.deleteOnExit(dir);
this.cp = new ClassFactory(dir, cl);
behaviourService = BehaviourProviderService.newInstance(cp);
Collection> baseClassRoles = mapper.getConceptClasses();
this.baseClassRoles = new ArrayList>(baseClassRoles.size());
for (Class> base : baseClassRoles) {
try {
// ensure the base class has a default constructor
base.getConstructor();
this.baseClassRoles.add(base);
} catch (NoSuchMethodException e) {
logger.warn("Concept will only be mergable: {}", base);
}
}
blank = resolveBlankEntity(EMPTY_SET);
} catch (IOException e) {
throw new ObjectStoreConfigException(e);
}
}
public RoleMapper getRoleMapper() {
return mapper;
}
public PropertyMapper getPropertyMapper() {
return properties;
}
public ClassLoader getClassLoader() {
return cp;
}
public Class> resolveBlankEntity() {
return blank;
}
public Class> resolveBlankEntity(Set types) {
Class> proxy = multiples.get(types);
if (proxy != null)
return proxy;
Collection> roles = new ArrayList>();
proxy = resolveRoles(mapper.findRoles(types, roles));
multiples.putIfAbsent(types, proxy);
return proxy;
}
public Class> resolveEntity(URI resource) {
if (resource != null && mapper.isIndividualRolesPresent(resource))
return resolveIndividualEntity(resource, EMPTY_SET);
return resolveBlankEntity();
}
public Class> resolveEntity(URI resource, Set types) {
if (resource != null && mapper.isIndividualRolesPresent(resource))
return resolveIndividualEntity(resource, types);
return resolveBlankEntity(types);
}
private Class> resolveIndividualEntity(URI resource, Collection types) {
Collection> roles = new ArrayList>();
roles = mapper.findIndividualRoles(resource, roles);
roles = mapper.findRoles(types, roles);
return resolveRoles(roles);
}
private Class> resolveRoles(Collection> roles) {
try {
String className = getJavaClassName(roles);
return getComposedBehaviours(className, roles);
} catch (Exception e) {
List roleNames = new ArrayList();
for (Class> f : roles) {
roleNames.add(f.getSimpleName());
}
throw new ObjectCompositionException(e.toString()
+ " for entity with roles: " + roleNames, e);
}
}
private Class> getComposedBehaviours(String className,
Collection> roles) throws Exception {
synchronized (cp) {
try {
Class> loadedClass = cp.classForName(className);
logger.debug("Proxy " + className + " found.");
List> types = new ArrayList<>(roles.size());
types.addAll(roles);
types = removeSuperClasses(types);
Collection behaviours = getAllBehaviours(types);
for (BehaviourFactory behaviour : behaviours) {
ClassComposer.populateBehaviourField(behaviour, loadedClass, className, behaviour);
}
return loadedClass;
} catch (ClassNotFoundException e1) {
logger.debug("Proxy " + className + " not found.");
return composeBehaviours(className, roles);
}
}
}
private Set getConcreteBehaviours(Collection> roles) {
Set concretes = new HashSet<>();
for (Class> role : roles) {
if(!role.isInterface() && !baseClassRoles.contains(role) && !isAbstract(role.getModifiers())) {
try {
concretes.add(new BehaviourConstructor(role));
} catch (NoSuchMethodException ignored) {
// ignore
}
}
}
return concretes;
}
private Set> getBaseRoles(Collection> roles) {
Set> bases = new HashSet<>();
for (Class> role : roles) {
if (!role.isInterface() && baseClassRoles.contains(role)) {
bases.add(role);
}
}
return bases;
}
private Set getAllBehaviours(Collection> roles) throws IOException {
Set allBehaviours = new HashSet<>();
allBehaviours.addAll(getConcreteBehaviours(roles));
allBehaviours.addAll(behaviourService.findImplementations(properties, roles, getBaseRoles(roles)));
return allBehaviours;
}
private Class> composeBehaviours(String className,
Collection> roles) throws Exception {
List> types = new ArrayList>(roles.size());
types.addAll(roles);
types = removeSuperClasses(types);
ClassComposer cc = new ClassComposer(className, types.size());
cc.setClassFactory(cp);
Set> behaviours = new LinkedHashSet>(types.size());
Set concretes = new LinkedHashSet(types.size());
Set> bases = new LinkedHashSet>();
Class> baseClass = Object.class;
for (Class> role : types) {
if (role.isInterface()) {
cc.addInterface(role);
} else {
if (baseClassRoles.contains(role)) {
if (baseClass != null && baseClass.isAssignableFrom(role)) {
baseClass = role;
} else if (!role.equals(baseClass)) {
baseClass = null;
}
bases.add(role);
} else if (!isAbstract(role.getModifiers())) {
try {
concretes.add(new BehaviourConstructor(role));
} catch (NoSuchMethodException e) {
// ignore
}
}
behaviours.add(role);
}
}
if (baseClass == null) {
logger.error("Cannot compose multiple concept classes: " + types);
} else {
cc.setBaseClass(baseClass);
}
Set> allRoles = new HashSet>(behaviours.size() + cc.getInterfaces().size());
allRoles.addAll(behaviours);
allRoles.addAll(cc.getInterfaces());
PropertyMapper pm = properties;
cc.addAllBehaviours(concretes);
cc.addAllBehaviours(behaviourService.findImplementations(pm, allRoles, bases));
return cc.compose(className);
}
private List> removeSuperClasses(List> classes) {
for (int i = classes.size() - 1; i >= 0; i--) {
Class> c = classes.get(i);
for (int j = classes.size() - 1; j >= 0; j--) {
Class> d = classes.get(j);
if (i != j && c.isAssignableFrom(d)
&& c.isInterface() == d.isInterface()) {
classes.remove(i);
break;
}
}
}
return classes;
}
private String getJavaClassName(Collection> javaClasses) {
String phex = packagesToHexString(javaClasses);
String chex = classesToHexString(javaClasses);
return PKG_PREFIX + phex + "." + CLASS_PREFIX + chex;
}
private String packagesToHexString(Collection> javaClasses) {
TreeSet names = new TreeSet();
for (Class> clazz : javaClasses) {
if (clazz.getPackage() != null) {
names.add(clazz.getPackage().getName());
}
}
return toHexString(names);
}
private String classesToHexString(Collection> javaClasses) {
TreeSet names = new TreeSet();
for (Class> clazz : javaClasses) {
names.add(clazz.getName());
}
return toHexString(names);
}
private String toHexString(TreeSet names) {
long hashCode = 0;
for (String name : names) {
hashCode = 31 * hashCode + name.hashCode();
}
return Long.toHexString(hashCode);
}
public ClassFactory getClassFactory() {
return cp;
}
}