All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.compass.spring.LocalCompassBean Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
/*
 * Copyright 2004-2008 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.compass.spring;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.compass.core.Compass;
import org.compass.core.CompassException;
import org.compass.core.config.CompassConfiguration;
import org.compass.core.config.CompassConfigurationFactory;
import org.compass.core.config.CompassEnvironment;
import org.compass.core.config.InputStreamMappingResolver;
import org.compass.core.converter.Converter;
import org.compass.core.lucene.LuceneEnvironment;
import org.compass.core.lucene.engine.store.jdbc.ExternalDataSourceProvider;
import org.compass.core.spi.InternalCompass;
import org.compass.core.util.ClassUtils;
import org.compass.spring.transaction.SpringSyncTransactionFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.transaction.PlatformTransactionManager;

/**
 * @author kimchy
 */
public class LocalCompassBean implements FactoryBean, InitializingBean, DisposableBean, BeanNameAware, ApplicationContextAware, BeanClassLoaderAware {

    protected static final Log log = LogFactory.getLog(LocalCompassBean.class);

    private Resource connection;

    private Resource configLocation;

    private String mappingScan;

    private Resource[] configLocations;

    private Resource[] resourceLocations;

    private Resource[] resourceJarLocations;

    private Resource[] resourceDirectoryLocations;

    private String[] classMappings;

    private InputStreamMappingResolver[] mappingResolvers;

    private Properties compassSettings;

    private Map settings;

    private DataSource dataSource;

    private PlatformTransactionManager transactionManager;

    private Map convertersByName;

    private Compass compass;

    private String beanName;

    private ClassLoader classLoader;

    private ApplicationContext applicationContext;

    private CompassConfiguration config;

    private LocalCompassBeanPostProcessor postProcessor;

    /**
     * Allows to register a post processor for the Compass configuration.
     */
    public void setPostProcessor(LocalCompassBeanPostProcessor postProcessor) {
        this.postProcessor = postProcessor;
    }

    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    /**
     * Sets an optional connection based on Spring Resource
     * abstraction. Will be used if none is set as part of other possible
     * configuration of Compass connection.
     * 

* Will use Resource#getFile in order to get the absolute * path. */ public void setConnection(Resource connection) { this.connection = connection; } /** * Set the location of the Compass XML config file, for example as classpath * resource "classpath:compass.cfg.xml". *

* Note: Can be omitted when all necessary properties and mapping resources * are specified locally via this bean. */ public void setConfigLocation(Resource configLocation) { this.configLocation = configLocation; } /** * Set the location of the Compass XML config file, for example as classpath * resource "classpath:compass.cfg.xml". *

* Note: Can be omitted when all necessary properties and mapping resources * are specified locally via this bean. */ public void setConfigLocations(Resource[] configLocations) { this.configLocations = configLocations; } /** * @see org.compass.core.config.CompassConfiguration#addScan(String) */ public void setMappingScan(String basePackage) { this.mappingScan = basePackage; } public void setCompassSettings(Properties compassSettings) { this.compassSettings = compassSettings; } public void setSettings(Map settings) { this.settings = settings; } /** * Set locations of Compass resource files (mapping and common metadata), * for example as classpath resource "classpath:example.cpm.xml". Supports * any resource location via Spring's resource abstraction, for example * relative paths like "WEB-INF/mappings/example.hbm.xml" when running in an * application context. *

* Can be used to add to mappings from a Compass XML config file, or to * specify all mappings locally. */ public void setResourceLocations(Resource[] resourceLocations) { this.resourceLocations = resourceLocations; } /** * Set locations of jar files that contain Compass resources, like * "WEB-INF/lib/example.jar". *

* Can be used to add to mappings from a Compass XML config file, or to * specify all mappings locally. */ public void setResourceJarLocations(Resource[] resourceJarLocations) { this.resourceJarLocations = resourceJarLocations; } /** * Set locations of directories that contain Compass mapping resources, like * "WEB-INF/mappings". *

* Can be used to add to mappings from a Compass XML config file, or to * specify all mappings locally. */ public void setResourceDirectoryLocations(Resource[] resourceDirectoryLocations) { this.resourceDirectoryLocations = resourceDirectoryLocations; } /** * Sets the fully qualified class names for mappings. Useful when using annotations * for example. Will also try to load the matching "[Class].cpm.xml" file. */ public void setClassMappings(String[] classMappings) { this.classMappings = classMappings; } /** * Sets the mapping resolvers the resolved Compass mapping definitions. */ public void setMappingResolvers(InputStreamMappingResolver[] mappingResolvers) { this.mappingResolvers = mappingResolvers; } /** * Sets a DataSource to be used when the index is stored within a database. * The data source must be used with {@link org.compass.core.lucene.engine.store.jdbc.ExternalDataSourceProvider} * for externally configured data sources (such is the case some of the time with spring). If set, Compass data source provider * does not have to be set, since it will automatically default to ExternalDataSourceProvider. If the * compass data source provider is set as a compass setting, it will be used. *

* Note, that it will be automatically wrapped with Spring's TransactionAwareDataSourceProxy if not * already wrapped by one. * {@link org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy}. *

* Also note that setting the data source is not enough to configure Compass to store the index * within the database, the Compass connection string should also be set to jdbc://. */ public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; if (!(dataSource instanceof TransactionAwareDataSourceProxy)) { this.dataSource = new TransactionAwareDataSourceProxy(dataSource); } } /** * Sets Spring PlatformTransactionManager to be used with compass. If using * {@link org.compass.spring.transaction.SpringSyncTransactionFactory}, it must be set. */ public void setTransactionManager(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } /** * Sets a map of global converters to be registered with compass. The map key will be * the name that the converter will be registered against, and the value should be the * Converter itself (natuarally configured using spring DI). */ public void setConvertersByName(Map convertersByName) { this.convertersByName = convertersByName; } public void setCompassConfiguration(CompassConfiguration config) { this.config = config; } public void afterPropertiesSet() throws Exception { CompassConfiguration config = this.config; if (config == null) { config = newConfiguration(); } if (classLoader != null) { config.setClassLoader(getClassLoader()); } if (this.configLocation != null) { config.configure(this.configLocation.getURL()); } if (this.configLocations != null) { for (Resource configLocation1 : configLocations) { config.configure(configLocation1.getURL()); } } if (this.mappingScan != null) { config.addScan(this.mappingScan); } if (this.compassSettings != null) { config.getSettings().addSettings(this.compassSettings); } if (this.settings != null) { config.getSettings().addSettings(this.settings); } if (resourceLocations != null) { for (Resource resourceLocation : resourceLocations) { config.addInputStream(resourceLocation.getInputStream(), resourceLocation.getFilename()); } } if (resourceJarLocations != null) { for (Resource resourceJarLocation : resourceJarLocations) { config.addJar(resourceJarLocation.getFile()); } } if (classMappings != null) { for (String classMapping : classMappings) { config.addClass(ClassUtils.forName(classMapping, getClassLoader())); } } if (resourceDirectoryLocations != null) { for (Resource resourceDirectoryLocation : resourceDirectoryLocations) { File file = resourceDirectoryLocation.getFile(); if (!file.isDirectory()) { throw new IllegalArgumentException("Resource directory location [" + resourceDirectoryLocation + "] does not denote a directory"); } config.addDirectory(file); } } if (mappingResolvers != null) { for (InputStreamMappingResolver mappingResolver : mappingResolvers) { config.addMappingResolver(mappingResolver); } } if (dataSource != null) { ExternalDataSourceProvider.setDataSource(dataSource); if (config.getSettings().getSetting(LuceneEnvironment.JdbcStore.DataSourceProvider.CLASS) == null) { config.getSettings().setSetting(LuceneEnvironment.JdbcStore.DataSourceProvider.CLASS, ExternalDataSourceProvider.class.getName()); } } String compassTransactionFactory = config.getSettings().getSetting(CompassEnvironment.Transaction.FACTORY); if (compassTransactionFactory == null && transactionManager != null) { // if the transaciton manager is set and a transcation factory is not set, default to the SpringSync one. config.getSettings().setSetting(CompassEnvironment.Transaction.FACTORY, SpringSyncTransactionFactory.class.getName()); } if (compassTransactionFactory != null && compassTransactionFactory.equals(SpringSyncTransactionFactory.class.getName())) { if (transactionManager == null) { throw new IllegalArgumentException("When using SpringSyncTransactionFactory the transactionManager property must be set"); } } SpringSyncTransactionFactory.setTransactionManager(transactionManager); if (convertersByName != null) { for (Map.Entry entry : convertersByName.entrySet()) { config.registerConverter(entry.getKey(), entry.getValue()); } } if (config.getSettings().getSetting(CompassEnvironment.NAME) == null) { config.getSettings().setSetting(CompassEnvironment.NAME, beanName); } if (config.getSettings().getSetting(CompassEnvironment.CONNECTION) == null && connection != null) { config.getSettings().setSetting(CompassEnvironment.CONNECTION, connection.getFile().getAbsolutePath()); } if (applicationContext != null) { String[] names = applicationContext.getBeanNamesForType(PropertyPlaceholderConfigurer.class); for (String name : names) { try { PropertyPlaceholderConfigurer propConfigurer = (PropertyPlaceholderConfigurer) applicationContext.getBean(name); Method method = findMethod(propConfigurer.getClass(), "mergeProperties"); method.setAccessible(true); Properties props = (Properties) method.invoke(propConfigurer); method = findMethod(propConfigurer.getClass(), "convertProperties", Properties.class); method.setAccessible(true); method.invoke(propConfigurer, props); method = findMethod(propConfigurer.getClass(), "parseStringValue", String.class, Properties.class, Set.class); method.setAccessible(true); String nullValue = null; try { Field field = propConfigurer.getClass().getDeclaredField("nullValue"); field.setAccessible(true); nullValue = (String) field.get(propConfigurer); } catch (NoSuchFieldException e) { // no field (old spring version) } for (Map.Entry entry : config.getSettings().getProperties().entrySet()) { String key = (String) entry.getKey(); String value = (String) entry.getValue(); value = (String) method.invoke(propConfigurer, value, props, new HashSet()); config.getSettings().setSetting(key, value.equals(nullValue) ? null : value); } } catch (Exception e) { log.debug("Failed to apply property placeholder defined in bean [" + name + "]", e); } } } if (postProcessor != null) { postProcessor.process(config); } this.compass = newCompass(config); this.compass = (Compass) Proxy.newProxyInstance(SpringCompassInvocationHandler.class.getClassLoader(), new Class[]{InternalCompass.class}, new SpringCompassInvocationHandler(this.compass)); } protected CompassConfiguration newConfiguration() { return CompassConfigurationFactory.newConfiguration(); } protected Compass newCompass(CompassConfiguration config) throws CompassException { return config.buildCompass(); } public Object getObject() throws Exception { return this.compass; } public Class getObjectType() { return (compass != null) ? compass.getClass() : Compass.class; } public boolean isSingleton() { return true; } public void destroy() throws Exception { this.compass.close(); } protected ClassLoader getClassLoader() { if (classLoader != null) { return classLoader; } return Thread.currentThread().getContextClassLoader(); } private Method findMethod(Class clazz, String methodName, Class ... parameterTypes) { if (clazz.equals(Object.class)) { return null; } try { return clazz.getDeclaredMethod(methodName, parameterTypes); } catch (NoSuchMethodException e) { return findMethod(clazz.getSuperclass(), methodName, parameterTypes); } } /** * Invocation handler that handles close methods. */ private class SpringCompassInvocationHandler implements InvocationHandler { private static final String GET_TARGET_COMPASS_METHOD_NAME = "getTargetCompass"; private static final String CLONE_METHOD = "clone"; private Compass targetCompass; public SpringCompassInvocationHandler(Compass targetCompass) { this.targetCompass = targetCompass; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // Invocation on ConnectionProxy interface coming in... if (method.getName().equals(GET_TARGET_COMPASS_METHOD_NAME)) { return compass; } if (method.getName().equals(CLONE_METHOD) && args.length == 1) { if (dataSource != null) { ExternalDataSourceProvider.setDataSource(dataSource); } SpringSyncTransactionFactory.setTransactionManager(transactionManager); } // Invoke method on target connection. try { return method.invoke(targetCompass, args); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy