FileDocCategorySizeDatePackage
ProvidedServiceConfig.javaAPI DocApache Lucene 2.1.011910Wed Feb 14 10:46:06 GMT 2007org.apache.lucene.gdata.server.registry

ProvidedServiceConfig.java

/** 
 * Copyright 2004 The Apache Software Foundation 
 * 
 * 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.apache.lucene.gdata.server.registry;

import java.lang.reflect.Constructor;

import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.gdata.search.config.IndexSchema;
import org.apache.lucene.gdata.utils.Pool;
import org.apache.lucene.gdata.utils.PoolObjectFactory;
import org.apache.lucene.gdata.utils.SimpleObjectPool;

import com.google.gdata.data.BaseEntry;
import com.google.gdata.data.BaseFeed;
import com.google.gdata.data.ExtensionProfile;

/**
 * Standard implementation of
 * {@link org.apache.lucene.gdata.server.registry.ProvidedService} to be used
 * inside the
 * {@link org.apache.lucene.gdata.server.registry.GDataServerRegistry}
 * <p>
 * ExtensionProfiles are used to generate and parse xml by the gdata api. For
 * that case all methods are synchronized. This will slow down the application
 * when performing lots of xml generation concurrently. For that case the
 * extensionProfile for a specific service will be pooled and reused.
 * </p>
 * 
 * 
 * @author Simon Willnauer
 * 
 */
@Scope(scope = Scope.ScopeType.REQUEST)
public class ProvidedServiceConfig implements ProvidedService, ScopeVisitor {
    private final static Log LOG = LogFactory
            .getLog(ProvidedServiceConfig.class);

    private static final int DEFAULT_POOL_SIZE = 5;
    private IndexSchema indexSchema;
    /*
     * To ensure a extension profile instance will not be shared within multiple
     * threads each thread requesting a config will have one instance for the
     * entire request.
     */
    protected final ThreadLocal<ExtensionProfile> extProfThreadLocal = new ThreadLocal<ExtensionProfile>();

    /*
     * ExtensionProfiles are used to generate and parse xml by the gdata api.
     * For that case all methodes are synchronized. This will slow down the
     * application when performing lots of xml generation concurrently. for that
     * case the extensionProfile for a specific service will be pooled and
     * reused.
     */
    private Pool<ExtensionProfile> profilPool;

    private String serviceName;

    private Class<? extends BaseEntry> entryType;

    private Class<? extends BaseFeed> feedType;

    private ExtensionProfile extensionProfile;

    private int poolSize = DEFAULT_POOL_SIZE;
    
    private Templates transformerTemplate;
    

    /**
     * @return Returns the poolSize.
     */
    public int getPoolSize() {
        return this.poolSize;
    }

    /**
     * @param poolSize
     *            The poolSize to set.
     */
    public void setPoolSize(int poolSize) {
        
        this.poolSize = poolSize >= DEFAULT_POOL_SIZE ? poolSize
                : DEFAULT_POOL_SIZE;
    }

    /**
     * Default constructor to instantiate via reflection
     */
    public ProvidedServiceConfig() {
        try {
            GDataServerRegistry.getRegistry().registerScopeVisitor(this);
        } catch (RegistryException e) {
            throw new RuntimeException("Can not register ScopeVisitor -- "
                    + e.getMessage(), e);
        }
    }

    /**
     * @see org.apache.lucene.gdata.server.registry.ProvidedService#getFeedType()
     */
    public Class getFeedType() {
        return this.feedType;
    }

    /**
     * @param feedType
     *            The feedType to set.
     */
    public void setFeedType(Class feedType) {
        this.feedType = feedType;
    }

    /**
     * @see org.apache.lucene.gdata.server.registry.ProvidedService#getExtensionProfile()
     */
    public ExtensionProfile getExtensionProfile() {
        ExtensionProfile ext = this.extProfThreadLocal.get();
        if (ext != null) {
            return ext;
        }
        if(this.extensionProfile == null)
            return null;
        if (this.profilPool == null)
            createProfilePool();
        ext = this.profilPool.aquire();
        this.extProfThreadLocal.set(ext);
        return ext;
    }

    /**
     * @param extensionProfil -
     *            the extension profile for this feed configuration
     */
    @SuppressWarnings("unchecked")
    public void setExtensionProfile(ExtensionProfile extensionProfil) {
        if (extensionProfil == null)
            throw new IllegalArgumentException(
                    "ExtensionProfile  must not be null");
        if (this.extensionProfile != null)
            return;
        this.extensionProfile = extensionProfil;

    }

    private void createProfilePool() {
        if (LOG.isInfoEnabled())
            LOG.info("Create ExtensionProfile pool with pool size:"
                    + this.poolSize + " for service " + this.serviceName);
        this.profilPool = new SimpleObjectPool<ExtensionProfile>(this.poolSize,
                new ExtensionProfileFactory<ExtensionProfile>(
                        this.extensionProfile.getClass(),this.entryType,this.feedType));
    }

    /**
     * TODO add comment
     * 
     * @param <E>
     * @param extensionProfileClass
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public <E extends ExtensionProfile> void setExtensionProfileClass(
            Class<E> extensionProfileClass) throws InstantiationException,
            IllegalAccessException {
        if (extensionProfileClass == null)
            throw new IllegalArgumentException(
                    "ExtensionProfile class must not be null");

        setExtensionProfile(extensionProfileClass.newInstance());

    }

    /**
     * @see org.apache.lucene.gdata.server.registry.ProvidedService#getEntryType()
     */
    public Class getEntryType() {
        return this.entryType;
    }

    /**
     * @param entryType
     */
    public void setEntryType(Class entryType) {
        this.entryType = entryType;
    }

    /**
     * @see org.apache.lucene.gdata.server.registry.ProvidedService#getName()
     */
    public String getName() {
        return this.serviceName;
    }

    /**
     * @param serviceName
     */
    public void setName(String serviceName) {
        this.serviceName = serviceName;
    }

    /**
     * @see org.apache.lucene.gdata.server.registry.ProvidedService#destroy()
     */
    public void destroy() {
        if (this.profilPool != null)
            this.profilPool.destroy();
        if (LOG.isInfoEnabled())
            LOG.info("Destroy Service " + this.serviceName
                    + " -- release all resources");
        this.feedType = null;
        this.entryType = null;
        this.extensionProfile = null;
    }

    private static class ExtensionProfileFactory<Type extends ExtensionProfile>
            implements PoolObjectFactory<Type> {
        private final Class<? extends ExtensionProfile> clazz;

        private final Constructor<? extends ExtensionProfile> constructor;

        private static final Object[] constArray = new Object[0];
        
        private BaseEntry entry;
        private BaseFeed feed;

        ExtensionProfileFactory(Class<? extends ExtensionProfile> clazz, Class<? extends BaseEntry> entryClass, Class<? extends BaseFeed> feedClass) {
            this.clazz = clazz;
            try {
                this.constructor = clazz.getConstructor(new Class[0]);
                this.entry = entryClass.newInstance();
                this.feed = feedClass.newInstance();
            } catch (Exception e) {
                throw new IllegalArgumentException(
                        "The given class has no default constructor -- can not use as a ExtensionProfile -- "
                                + this.clazz.getName(), e);
            }
        }

        /**
         * @see org.apache.lucene.gdata.utils.PoolObjectFactory#getInstance()
         */
        @SuppressWarnings("unchecked")
        public Type getInstance() {

            try {
                Type retValue = (Type) this.constructor.newInstance(constArray);
                this.entry.declareExtensions(retValue);
                this.feed.declareExtensions(retValue);
                return retValue; 
            } catch (Exception e) {
                throw new RuntimeException(
                        "Can not instantiate new ExtensionProfile -- ", e);

            }
        }

        /**
         * @param type -
         *            the ExtensionProfile to destroy
         * @see org.apache.lucene.gdata.utils.PoolObjectFactory#destroyInstance(Object)
         */
        public void destroyInstance(Type type) {
            //
        }

    }

    /**
     * @see org.apache.lucene.gdata.server.registry.ScopeVisitor#visiteInitialize()
     */
    public void visiteInitialize() {
        if(this.profilPool == null)
            createProfilePool();
        /*
         * don't set a extension profile for each thread. The current thread
         * might use another service and does not need the extension profile of
         * this service
         */
    }

    /**
     * @see org.apache.lucene.gdata.server.registry.ScopeVisitor#visiteDestroy()
     */
    public void visiteDestroy() {
        /*
         * Check every thread after request destroyed to release all profiles to
         * the pool
         */
        ExtensionProfile ext = this.extProfThreadLocal.get();
        if (ext == null) {
            if(LOG.isDebugEnabled())
            LOG.debug("ThreadLocal owns no ExtensionProfile in requestDestroy for service "
                            + this.serviceName);
            return;
        }
        this.extProfThreadLocal.set(null);
        this.profilPool.release(ext);
    }

    /**
     * @return Returns the indexSchema.
     */
    public IndexSchema getIndexSchema() {
        return this.indexSchema;
    }

    /**
     * @param indexSchema The indexSchema to set.
     */
    public void setIndexSchema(IndexSchema indexSchema) {
        this.indexSchema = indexSchema;
        if(this.indexSchema != null)
            this.indexSchema.setName(this.serviceName);
    }

    /**
     * @see org.apache.lucene.gdata.server.registry.ProvidedService#getTransformTemplate()
     */
    public Templates getTransformTemplate() {
        
        return this.transformerTemplate;
    }
    
    /**
     * Sets and creates the preview transformer xslt template to provide a html formate for feeds and entries.
     * The given file name must be available in the classpath. 
     * @param filename - the name of the file in the classpath
     */
    public void setXsltStylesheet(String filename){
        if(filename == null || filename.length() == 0){
            LOG.info("No preview stylesheet configured for service "+this.serviceName);
            return;
        }
        
        TransformerFactory factory = TransformerFactory.newInstance();
        
        try {
            this.transformerTemplate = factory.newTemplates(new StreamSource(ProvidedServiceConfig.class.getResourceAsStream(filename.startsWith("/")?filename:"/"+filename)));
        } catch (TransformerConfigurationException e) {
            throw new RuntimeException("Can not compile xslt stylesheet path: "+filename,e);
        }
        
    }
    
}