package internal.org.springframework.content.cmis; import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.util.UUID; import internal.org.springframework.content.commons.utils.StoreUtils; import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryInfoImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.TypeDefinitionListImpl; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.ManagedList; import org.springframework.content.cmis.CmisDocument; import org.springframework.content.cmis.CmisFolder; import org.springframework.content.cmis.EnableCmis; import org.springframework.content.commons.repository.ContentStore; import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter; import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.core.type.filter.AssignableTypeFilter; import org.springframework.core.type.filter.TypeFilter; import org.springframework.data.repository.Repository; import org.springframework.data.repository.config.SpringDataAnnotationBeanNameGenerator; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import static java.lang.String.format; public class CmisRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware, BeanClassLoaderAware { public static final String CMIS_REPOSITORY_INFO = "cmisRepositoryInfo"; private static final String CMIS_TYPE_DEFINITION_LIST = "cmisTypeDefinitionList"; private @SuppressWarnings("null") /*@Nonnull*/ ResourceLoader resourceLoader; private @SuppressWarnings("null") /*@Nonnull*/ Environment environment; private ClassLoader classLoader; BeanDefinition cmisFolderBeanDefinition; BeanDefinition cmisFolderRepositoryBeanDefinition; BeanDefinition cmisDocumentBeanDefinition; BeanDefinition cmisDocumentRepositoryBeanDefinition; BeanDefinition cmisDocumentStoreBeanDefinition; /* * (non-Javadoc) * @see org.springframework.context.ResourceLoaderAware#setResourceLoader(org.springframework.core.io.ResourceLoader) */ @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } /* * (non-Javadoc) * @see org.springframework.context.EnvironmentAware#setEnvironment(org.springframework.core.env.Environment) */ @Override public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) { AnnotationAttributes attributes = new AnnotationAttributes(annotationMetadata.getAnnotationAttributes(EnableCmis.class.getName())); beanDefinitionRegistry.registerBeanDefinition(CMIS_REPOSITORY_INFO, createCmisRepositorInfoBeanDefinition(attributes, annotationMetadata)); BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(TypeDefinitionListImpl.class); ManagedList beanRefs = new ManagedList(); builder.addPropertyValue("list", beanRefs); beanDefinitionRegistry.registerBeanDefinition(CMIS_TYPE_DEFINITION_LIST, builder.getBeanDefinition()); String[] basePackages = attributes.getStringArray("basePackages"); cmisDocumentScan(basePackages); cmisFolderScan(basePackages); // create cmis:document type definition Class<?> entityClass = null; Class<?> repoClass = null; Class<?> storeClass = null; try { entityClass = ClassUtils.forName(cmisDocumentBeanDefinition.getBeanClassName(), classLoader); repoClass = ClassUtils.forName(cmisDocumentRepositoryBeanDefinition.getBeanClassName(), classLoader); storeClass = ClassUtils.forName(cmisDocumentStoreBeanDefinition.getBeanClassName(), classLoader); String typeDefBeanName = format("%sTypeDefinition", cmisDocumentBeanDefinition.getBeanClassName()); beanDefinitionRegistry.registerBeanDefinition(typeDefBeanName, createTypeDefinition(entityClass, repoClass, storeClass, annotationMetadata)); // add type definition to the list that will be given to the typedefinitionlist bean beanRefs.add(new RuntimeBeanReference(typeDefBeanName)); } catch (Throwable t) { } // create cmis:folder type definition try { entityClass = ClassUtils.forName(cmisFolderBeanDefinition.getBeanClassName(), classLoader); repoClass = ClassUtils.forName(cmisFolderRepositoryBeanDefinition.getBeanClassName(), classLoader); String typeDefBeanName = format("%sTypeDefinition", cmisFolderBeanDefinition.getBeanClassName()); beanDefinitionRegistry.registerBeanDefinition(typeDefBeanName, createTypeDefinition(entityClass, repoClass, null, annotationMetadata)); // add type definition to the list that will be given to the typedefinitionlist bean beanRefs.add(new RuntimeBeanReference(typeDefBeanName)); } catch (Throwable t) { } // create the cmis repository configuration (the root configuration object) beanDefinitionRegistry.registerBeanDefinition("CmisRepositoryConfiguration", createCmisRepositoryConfigurationDefinition(cmisFolderRepositoryBeanDefinition, cmisDocumentRepositoryBeanDefinition, cmisDocumentStoreBeanDefinition, annotationMetadata)); } void cmisDocumentScan(String... basePackages) { ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); scanner.addIncludeFilter(new AnnotationTypeFilter(CmisDocument.class)); for (String basePackage : basePackages) { for (BeanDefinition bd : scanner.findCandidateComponents(basePackage)) { cmisDocumentBeanDefinition = bd; CmisEntityRepositoryComponentProvider cmisRepoScanner = new CmisEntityRepositoryComponentProvider(bd.getBeanClassName(), classLoader); scanner.setResourceLoader(resourceLoader); scanner.setEnvironment(environment); for (int i =0; i < basePackages.length && cmisDocumentRepositoryBeanDefinition == null; i++) { cmisDocumentRepositoryBeanDefinition = cmisRepoScanner.findCandidateComponents(basePackages[i]) .stream() .findFirst() .get(); } CmisEntityStorageComponentProvider cmisStoreScanner = new CmisEntityStorageComponentProvider(bd.getBeanClassName(), classLoader); scanner.setResourceLoader(resourceLoader); scanner.setEnvironment(environment); for (int i =0; i < basePackages.length && cmisDocumentStoreBeanDefinition == null; i++) { cmisDocumentStoreBeanDefinition = cmisStoreScanner.findCandidateComponents(basePackages[i]) .stream() .findFirst() .get(); } } } } void cmisFolderScan(String... basePackages) { ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); scanner.addIncludeFilter(new AnnotationTypeFilter(CmisFolder.class)); for (String basePackage : basePackages) { for (BeanDefinition bd : scanner.findCandidateComponents(basePackage)) { if (bd.getBeanClassName().equals(CmisServiceBridge.Root.class.getName())) { continue; } cmisFolderBeanDefinition = bd; CmisEntityRepositoryComponentProvider cmisRepoScanner = new CmisEntityRepositoryComponentProvider(bd.getBeanClassName(), classLoader); scanner.setResourceLoader(resourceLoader); scanner.setEnvironment(environment); for (int i =0; i < basePackages.length && cmisFolderRepositoryBeanDefinition == null; i++) { cmisFolderRepositoryBeanDefinition = cmisRepoScanner.findCandidateComponents(basePackages[i]) .stream() .findFirst() .get(); } } } } BeanDefinition createCmisRepositorInfoBeanDefinition(AnnotationAttributes attributes, AnnotationMetadata annotationMetadata) { BeanDefinitionBuilder repoInfoBuilder = BeanDefinitionBuilder.genericBeanDefinition(RepositoryInfoImpl.class); repoInfoBuilder.getRawBeanDefinition().setSource(annotationMetadata); String id = attributes.getString("id"); if (StringUtils.isEmpty(id)) { id = UUID.randomUUID().toString(); } repoInfoBuilder.addPropertyValue("id", id); repoInfoBuilder.addPropertyValue("name", attributes.getString("name")); repoInfoBuilder.addPropertyValue("description", attributes.getString("description")); repoInfoBuilder.addPropertyValue("vendorName", attributes.getString("vendorName")); repoInfoBuilder.addPropertyValue("productName", attributes.getString("productName")); repoInfoBuilder.addPropertyValue("productVersion", attributes.getString("productVersion")); repoInfoBuilder.addPropertyValue("rootFolder", "@root@"); return repoInfoBuilder.getBeanDefinition(); } BeanDefinition createCmisRepositoryConfigurationDefinition( BeanDefinition cmisFolderRepoBeanDef, BeanDefinition cmisDocumentRepoBeanDef, BeanDefinition cmisDocumentStoreBeanDefinition, AnnotationMetadata annotationMetadata) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CmisRepositoryConfigurationImpl.class); builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); builder.getRawBeanDefinition().setSource(annotationMetadata); String beanName = new SpringDataAnnotationBeanNameGenerator().generateBeanName(cmisFolderRepoBeanDef); builder.addPropertyReference("cmisFolderRepository", beanName); beanName = new SpringDataAnnotationBeanNameGenerator().generateBeanName(cmisDocumentRepoBeanDef); builder.addPropertyReference("cmisDocumentRepository", beanName); beanName = StoreUtils.getStoreBeanName(cmisDocumentStoreBeanDefinition); builder.addPropertyReference("cmisDocumentStorage", beanName); builder.addPropertyReference("cmisRepositoryInfo", CMIS_REPOSITORY_INFO); builder.addPropertyReference("cmisTypeDefinitionList", CMIS_TYPE_DEFINITION_LIST); return builder.getBeanDefinition(); } BeanDefinition createTypeDefinition(Class<?> bd, Class<?> rbd, Class<?> sbd, AnnotationMetadata annotationMetadata) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CmisTypeDefinitionFactoryBean.class); builder.getRawBeanDefinition().setSource(annotationMetadata); try { builder.addPropertyValue("entityClass", bd); builder.addPropertyValue("repoClass", rbd); builder.addPropertyValue("storeClass", sbd); } catch (Throwable t) { } return builder.getBeanDefinition(); } static class CmisEntityRepositoryComponentProvider extends ClassPathScanningCandidateComponentProvider { public CmisEntityRepositoryComponentProvider(String cmisEntityClassName, ClassLoader classLoader) { AssignableTypeFilter assignableTypeFilter = new AssignableTypeFilter(Repository.class); CmisEntityTypeFilter cmisEntityTypeFilter = new CmisEntityTypeFilter(cmisEntityClassName, classLoader); this.addIncludeFilter(new AllTypeFilter(cmisEntityTypeFilter, assignableTypeFilter)); } @Override protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { return true; } } static class CmisEntityStorageComponentProvider extends ClassPathScanningCandidateComponentProvider { public CmisEntityStorageComponentProvider(String cmisEntityClassName, ClassLoader classLoader) { AssignableTypeFilter assignableTypeFilter = new AssignableTypeFilter(ContentStore.class); CmisEntityTypeFilter cmisEntityTypeFilter = new CmisEntityTypeFilter(cmisEntityClassName, classLoader); this.addIncludeFilter(new AllTypeFilter(cmisEntityTypeFilter, assignableTypeFilter)); } @Override protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { return true; } } static class AllTypeFilter implements TypeFilter { private final TypeFilter[] delegates; public AllTypeFilter(TypeFilter... delegates) { Assert.notNull(delegates, "TypeFilter delegates must not be null!"); this.delegates = delegates; } public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { for (TypeFilter filter : delegates) { if (!filter.match(metadataReader, metadataReaderFactory)) { return false; } } return true; } } static class CmisEntityTypeFilter extends AbstractTypeHierarchyTraversingFilter { private final String cmisEntityClassName; private final ClassLoader classLoader; protected CmisEntityTypeFilter(String cmisEntityClassName, ClassLoader classLoader) { super(false, false); Assert.hasLength(cmisEntityClassName, "cmisEntityClassName cant be null or empty"); this.cmisEntityClassName = cmisEntityClassName; this.classLoader = classLoader; } @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { return (hasCmisEntityType(metadataReader.getClassMetadata().getClassName())); } private boolean hasCmisEntityType(String interfaceName) { try { Class<?> iface = ClassUtils.forName(interfaceName, classLoader); String entityClassName = ((ParameterizedType)iface.getGenericInterfaces()[0]).getActualTypeArguments()[0].getTypeName(); return (cmisEntityClassName.equals(entityClassName)); } catch (Throwable t) { // no match } return false; } } }