FileDocCategorySizeDatePackage
DB4oController.javaAPI DocApache Lucene 2.1.013227Wed Feb 14 10:46:04 GMT 2007org.apache.lucene.gdata.storage.db4o

DB4oController.java

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.storage.db4o;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.security.NoSuchAlgorithmException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.gdata.data.GDataAccount;
import org.apache.lucene.gdata.data.ServerBaseFeed;
import org.apache.lucene.gdata.server.registry.Component;
import org.apache.lucene.gdata.server.registry.ComponentType;
import org.apache.lucene.gdata.server.registry.Scope;
import org.apache.lucene.gdata.server.registry.ScopeVisitor;
import org.apache.lucene.gdata.server.registry.configuration.Requiered;
import org.apache.lucene.gdata.storage.IDGenerator;
import org.apache.lucene.gdata.storage.Storage;
import org.apache.lucene.gdata.storage.StorageController;
import org.apache.lucene.gdata.storage.StorageException;
import org.apache.lucene.gdata.storage.db4o.DB4oStorage.DB4oEntry;
import org.apache.lucene.gdata.utils.Pool;
import org.apache.lucene.gdata.utils.PoolObjectFactory;
import org.apache.lucene.gdata.utils.SimpleObjectPool;

import com.db4o.Db4o;
import com.db4o.ObjectContainer;
import com.db4o.ObjectServer;
import com.google.gdata.data.BaseEntry;
import com.google.gdata.data.BaseFeed;

/**
 * The DB4o StorageContorller can be used as a persitence component for the
 * gdata-server. To use DB4o a third party jar needs to added to the lib
 * directory of the project. If the jar is not available in the lib directory
 * all db4o dependent class won't be included in the build.
 * <p>
 * If the jar is present in the lib directory this class can be configured as a
 * {@link org.apache.lucene.gdata.server.registry.ComponentType#STORAGECONTROLLER}
 * via the <i>gdata-config.xml</i> file. For detailed config documentation see
 * the wiki page.
 * </p>
 * <p>
 * The DB4oController can run as a client or as a server to serve other running
 * db4o clients in the network. To achive the best performance out of the db4o
 * caching layer connections to the server will be reused in a connection pool.
 * A connection will not be shared withing more than one thread. The controller
 * release one connection per request and returns the connection when the
 * request has been destroyed.
 * </p>
 * @see <a href="http://www.db4o.com">db4o website</a>
 * @see org.apache.lucene.gdata.utils.Pool
 * 
 * 
 * @author Simon Willnauer
 * 
 */
@Component(componentType = ComponentType.STORAGECONTROLLER)
@Scope(scope = Scope.ScopeType.REQUEST)
public class DB4oController implements StorageController, ScopeVisitor {
    private static final Log LOG = LogFactory.getLog(DB4oController.class);

    private final ThreadLocal<Storage> threadLocalStorage = new ThreadLocal<Storage>();

    private Pool<ObjectContainer> containerPool;

    private ObjectServer server;

    private final IDGenerator idGenerator;

    private boolean weakReferences;

    private boolean runAsServer;

    private int port;

    private String filePath;

    private String user;

    private String password;

    private String host;

    private int containerPoolSize;

    /**
     * @throws NoSuchAlgorithmException
     * 
     */
    public DB4oController() throws NoSuchAlgorithmException {

        this.idGenerator = new IDGenerator(15);

    }

    ObjectContainer releaseContainer() {
        return this.server.openClient();

    }

    /**
     * @see org.apache.lucene.gdata.storage.StorageController#destroy()
     */
    public void destroy() {
        this.containerPool.destroy();
        this.idGenerator.stopIDGenerator();
        this.server.close();
    }

    /**
     * @see org.apache.lucene.gdata.storage.StorageController#getStorage()
     */
    public Storage getStorage() throws StorageException {
       Storage retVal = this.threadLocalStorage.get();
        if (retVal != null)
            return retVal;

        retVal = new DB4oStorage(this.containerPool.aquire(), this);

        this.threadLocalStorage.set(retVal);
        return retVal;
    }

    /**
     * @see org.apache.lucene.gdata.server.registry.ServerComponent#initialize()
     */
    public void initialize() {
        if (LOG.isInfoEnabled())
            LOG.info("Initialize " + this.toString());

        Db4o.configure().objectClass(DB4oEntry.class).objectField("updated")
                .indexed(true);
        Db4o.configure().objectClass(BaseEntry.class).objectField("id")
                .indexed(true);
        Db4o.configure().objectClass(BaseFeed.class).objectField("id").indexed(
                true);
        Db4o.configure().objectClass(GDataAccount.class).objectField("name")
                .indexed(true);
        Db4o.configure().objectClass(ServerBaseFeed.class).cascadeOnDelete(
                false);
        Db4o.configure().objectClass(ServerBaseFeed.class)
                .maximumActivationDepth(0);
        Db4o.configure().objectClass(BaseFeed.class).minimumActivationDepth(1);
        Db4o.configure().objectClass(BaseEntry.class)
                .minimumActivationDepth(1);
        Db4o.configure().objectClass(BaseFeed.class).cascadeOnDelete(true);
        Db4o.configure().objectClass(DB4oEntry.class).cascadeOnDelete(true);
        Db4o.configure().objectClass(GDataAccount.class).cascadeOnDelete(true);
        Db4o.configure().weakReferences(this.weakReferences);
        Db4o.configure().optimizeNativeQueries(false);
        if (this.runAsServer) {
            this.server = Db4o.openServer(this.filePath, this.port);
            if(this.server == null)
                throw new RuntimeException("Can't create server at confiugred destination -- "+this.filePath);
            this.server.grantAccess(this.user, this.password);
        } else {
            InvocationHandler handler = new ObjectServerDecorator(this.user,
                    this.password, this.host, this.port);
            this.server = (ObjectServer) Proxy.newProxyInstance(this.getClass()
                    .getClassLoader(), new Class[] { ObjectServer.class },
                    handler);
        }

        PoolObjectFactory<ObjectContainer> factory = new ObjectContinerFactory(
                this.server);
        this.containerPool = new SimpleObjectPool<ObjectContainer>(
                this.containerPoolSize, factory);
        try {
            createAdminAccount();
        } catch (StorageException e) {
            LOG.error("Can not create admin account -- ",e);
        }
    }

    private void createAdminAccount() throws StorageException {
        GDataAccount adminAccount = GDataAccount.createAdminAccount();
        visiteInitialize();
        Storage sto = this.getStorage();
        try {
            sto.getAccount(adminAccount.getName());
        } catch (Exception e) {
            this.getStorage().storeAccount(adminAccount);
        } finally {
            visiteDestroy();
        }

    }

    
    /**
     * @see org.apache.lucene.gdata.storage.StorageController#releaseId()
     */
    public String releaseId(){
        try{
        return this.idGenerator.getUID();
        }catch (InterruptedException e) {
            throw new StorageException("ID producer has been interrupted",e);
        }
    }

    /**
     * @see org.apache.lucene.gdata.server.registry.ScopeVisitor#visiteInitialize()
     */
    public void visiteInitialize() {
        if (LOG.isInfoEnabled())
            LOG.info("Opened Storage -- request initialized");
        Storage storage = this.threadLocalStorage.get();
        if (storage != null) {
            LOG.warn("Storage already opened");
            return;
        }

        storage = new DB4oStorage(this.containerPool.aquire(), this);

        this.threadLocalStorage.set(storage);
    }

    /**
     * @see org.apache.lucene.gdata.server.registry.ScopeVisitor#visiteDestroy()
     */
    public void visiteDestroy() {
        Storage storage = this.threadLocalStorage.get();
        if (storage == null) {
            LOG.warn("no Storage opened -- threadlocal returned null");
            return;
        }
        this.containerPool.release(((DB4oStorage)storage).getContainer());
        this.threadLocalStorage.remove();
        if (LOG.isInfoEnabled())
            LOG.info("Closed Storage -- request destroyed");
    }

    private static class ObjectContinerFactory implements
            PoolObjectFactory<ObjectContainer> {
        private final ObjectServer server;

        ObjectContinerFactory(final ObjectServer server) {
            this.server = server;
        }

        /**
         * @see org.apache.lucene.gdata.utils.PoolObjectFactory#getInstance()
         */
        public ObjectContainer getInstance() {

            return this.server.openClient();
        }

        /**
         * @param type -
         *            object container to destroy (close)
         * @see org.apache.lucene.gdata.utils.PoolObjectFactory#destroyInstance(Object)
         */
        public void destroyInstance(ObjectContainer type) {
            type.close();
        }

    }

    /**
     * @return Returns the filePath.
     */
    public String getFilePath() {
        return this.filePath;
    }

    /**
     * @param filePath
     *            The filePath to set.
     */
    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }

    /**
     * @return Returns the host.
     */
    public String getHost() {
        return this.host;
    }

    /**
     * @param host
     *            The host to set.
     */
    @Requiered
    public void setHost(String host) {
        this.host = host;
    }

    /**
     * @return Returns the password.
     */
    public String getPassword() {
        return this.password;
    }

    /**
     * @param password
     *            The password to set.
     */
    @Requiered
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * @return Returns the port.
     */
    public int getPort() {
        return this.port;
    }

    /**
     * @param port
     *            The port to set.
     */
    @Requiered
    public void setPort(int port) {
        this.port = port;
    }

    /**
     * @return Returns the runAsServer.
     */
    public boolean isRunAsServer() {
        return this.runAsServer;
    }

    /**
     * @param runAsServer
     *            The runAsServer to set.
     */
    @Requiered
    public void setRunAsServer(boolean runAsServer) {
        this.runAsServer = runAsServer;
    }

    /**
     * @return Returns the user.
     */
    public String getUser() {
        return this.user;
    }

    /**
     * @param user
     *            The user to set.
     */
    @Requiered
    public void setUser(String user) {
        this.user = user;
    }

    /**
     * @return Returns the weakReferences.
     */
    public boolean isUseWeakReferences() {
        return this.weakReferences;
    }

    /**
     * @param weakReferences
     *            The weakReferences to set.
     */
    @Requiered
    public void setUseWeakReferences(boolean weakReferences) {
        this.weakReferences = weakReferences;
    }

    /**
     * @return Returns the containerPoolSize.
     */
    public int getContainerPoolSize() {
        return this.containerPoolSize;
    }

    /**
     * @param containerPoolSize
     *            The containerPoolSize to set.
     */
    @Requiered
    public void setContainerPoolSize(int containerPoolSize) {
        this.containerPoolSize = containerPoolSize < 1 ? 1 : containerPoolSize;
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder(this.getClass().getName())
                .append(" ");
        builder.append("host: ").append(this.host).append(" ");
        builder.append("port: ").append(this.port).append(" ");
        builder.append("pool size: ").append(this.containerPoolSize)
                .append(" ");
        builder.append("runs as server: ").append(this.runAsServer).append(" ");
        builder.append("use weak references: ").append(this.weakReferences)
                .append(" ");
        builder.append("user: ").append(this.user).append(" ");
        builder.append("password length: ").append(
                this.password == null ? "no password" : this.password.length())
                .append(" ");

        return builder.toString();
    }

}