org.collectionspace.services.nuxeo.client.java.NuxeoClientEmbedded.java Source code

Java tutorial

Introduction

Here is the source code for org.collectionspace.services.nuxeo.client.java.NuxeoClientEmbedded.java

Source

/*
 * (C) Copyright 2006-2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * Contributors:
 *     bstefanescu, jcarsique
 *
 * $Id$
 */

package org.collectionspace.services.nuxeo.client.java;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;

import javax.security.auth.login.AppConfigurationEntry;
import javax.transaction.TransactionManager;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.collectionspace.services.config.tenant.RepositoryDomainType;
import org.jboss.remoting.InvokerLocator;
import org.nuxeo.common.collections.ListenerList;
import org.nuxeo.ecm.core.api.repository.Repository;
import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
import org.nuxeo.ecm.core.api.repository.RepositoryInstanceHandler;
import org.nuxeo.ecm.core.api.repository.RepositoryManager;
import org.nuxeo.ecm.core.client.ConnectionListener;
import org.nuxeo.ecm.core.client.DefaultLoginHandler;
import org.nuxeo.ecm.core.client.LoginHandler;
import org.nuxeo.ecm.core.repository.RepositoryDescriptor;
import org.nuxeo.ecm.core.schema.SchemaManager;
import org.nuxeo.ecm.core.schema.SchemaManagerImpl;
import org.nuxeo.ecm.core.schema.TypeProvider;
import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.api.ServiceDescriptor;
import org.nuxeo.runtime.api.ServiceManager;
import org.nuxeo.runtime.api.login.LoginComponent;
import org.nuxeo.runtime.api.login.LoginService;
import org.nuxeo.runtime.api.login.SecurityDomain;
import org.nuxeo.runtime.config.AutoConfigurationService;
import org.nuxeo.runtime.remoting.RemotingService;
import org.nuxeo.runtime.services.streaming.StreamingService;
import org.nuxeo.runtime.transaction.TransactionHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
 *
 */
public final class NuxeoClientEmbedded {

    private Logger logger = LoggerFactory.getLogger(NuxeoClientEmbedded.class);

    private LoginHandler loginHandler;

    private final HashMap<String, RepositoryInstance> repositoryInstances;

    private final ListenerList connectionListeners;

    private InvokerLocator locator;

    private String serverName;

    private final AutoConfigurationService cfg;

    private RepositoryManager repositoryMgr;

    private boolean multiThreadedLogin = false;

    private static final NuxeoClientEmbedded instance = new NuxeoClientEmbedded();

    private static final Log log = LogFactory.getLog(NuxeoClientEmbedded.class);

    /**
     * Constructs a new NuxeoClient. NOTE: Using {@link #getInstance()} instead
     * of this constructor is recommended.
     */
    public NuxeoClientEmbedded() {
        connectionListeners = new ListenerList();
        cfg = new AutoConfigurationService();
        loginHandler = loginHandler == null ? new DefaultLoginHandler() : loginHandler;
        repositoryInstances = new HashMap<String, RepositoryInstance>();
    }

    public static NuxeoClientEmbedded getInstance() {
        return instance;
    }

    public void setMultiThreadedLogin(boolean useMultiThreadedLogin) {
        multiThreadedLogin = useMultiThreadedLogin;
    }

    public boolean getMultiThreadedLogin() {
        return multiThreadedLogin;
    }

    public synchronized void connect(String locator) throws Exception {
        if (this.locator != null) {
            throw new IllegalStateException("Client is already connected");
        }
        doConnect(AutoConfigurationService.createLocator(locator));
    }

    public synchronized void connect(InvokerLocator locator) throws Exception {
        if (this.locator != null) {
            throw new IllegalStateException("Client is already connected");
        }
        doConnect(locator);
    }

    public synchronized void connect(String host, int port) throws Exception {
        if (locator != null) {
            throw new IllegalStateException("Client is already connected");
        }
        doConnect(AutoConfigurationService.createLocator(host, port));
    }

    public synchronized void forceConnect(InvokerLocator locator) throws Exception {
        if (this.locator != null) {
            disconnect();
        }
        doConnect(locator);
    }

    public synchronized void forceConnect(String locator) throws Exception {
        if (this.locator != null) {
            disconnect();
        }
        doConnect(AutoConfigurationService.createLocator(locator));
    }

    public synchronized void forceConnect(String host, int port) throws Exception {
        if (locator != null) {
            disconnect();
        }
        doConnect(AutoConfigurationService.createLocator(host, port));
    }

    public synchronized void tryConnect(String host, int port) throws Exception {
        if (locator != null) {
            return; // do nothing
        }
        doConnect(AutoConfigurationService.createLocator(host, port));
    }

    public synchronized void tryConnect(String url) throws Exception {
        if (locator != null) {
            return; // do nothing
        }
        doConnect(AutoConfigurationService.createLocator(url));
    }

    public synchronized void tryConnect(InvokerLocator locator) throws Exception {
        if (this.locator != null) {
            return; // do nothing
        }
        doConnect(locator);
    }

    @Deprecated
    private void doConnect(InvokerLocator locator) throws Exception {
        this.locator = locator;
        try {
            cfg.load(locator);
            // FIXME TODO workaround to work with nxruntime core 1.3.3
            // --------------
            String newPort = Framework.getProperty("org.nuxeo.runtime.1.3.3.streaming.port");
            if (newPort != null) {
                StreamingService streamingService = (StreamingService) Framework.getRuntime()
                        .getComponent(StreamingService.NAME);
                // streaming config
                String oldLocator = streamingService.getServerLocator();
                int p = oldLocator.lastIndexOf(':');
                if (p > -1) {
                    String withoutPort = oldLocator.substring(0, p);
                    String serverLocator = withoutPort + ":" + newPort;
                    streamingService.stopManager();
                    streamingService.setServerLocator(serverLocator);
                    streamingService.setServer(false);
                    streamingService.startManager();
                }
            }
            // FIXME TODO workaround for remote services
            // -------------------------------
            schemaRemotingWorkaround(locator.getHost());
            // workaround for client login configuration - we need to make it
            // not multi threaded
            // TODO put an option for this in NuxeoClient
            if (!multiThreadedLogin) {
                LoginService ls = Framework.getService(LoginService.class);
                SecurityDomain sysDomain = ls.getSecurityDomain(LoginComponent.SYSTEM_LOGIN);
                SecurityDomain clientDomain = ls.getSecurityDomain(LoginComponent.CLIENT_LOGIN);
                adaptClientSecurityDomain(sysDomain);
                adaptClientSecurityDomain(clientDomain);
            }
            // ----------------
            //            login();
            if (!multiThreadedLogin) {
                throw new RuntimeException("This coode is dead and should never be called?"); //FIXME: REM - If you're reading this, this was left here by mistake and should be removed.
            }
        } catch (Exception e) {
            this.locator = null;
            throw e;
        }
        fireConnected(this);
    }

    public static void adaptClientSecurityDomain(SecurityDomain sd) {
        AppConfigurationEntry[] entries = sd.getAppConfigurationEntries();
        if (entries != null) {
            for (int i = 0; i < entries.length; i++) {
                AppConfigurationEntry entry = entries[i];
                if ("org.jboss.security.ClientLoginModule".equals(entry.getLoginModuleName())) {
                    Map<String, ?> opts = entry.getOptions();
                    Map<String, Object> newOpts = new HashMap<String, Object>(opts);
                    newOpts.put("multi-threaded", "false");
                    entries[i] = new AppConfigurationEntry(entry.getLoginModuleName(), entry.getControlFlag(),
                            entry.getOptions());
                }
            }
        }
    }

    /**
     * Workaround for being able to load schemas from remote
     * TODO integrate this in core
     */
    private static void schemaRemotingWorkaround(String host) throws Exception {
        ServiceManager serviceManager = Framework.getLocalService(ServiceManager.class);
        ServiceDescriptor sd = new ServiceDescriptor(TypeProvider.class, "core");
        sd.setLocator("%TypeProviderBean");
        serviceManager.registerService(sd);
        TypeProvider typeProvider = Framework.getService(TypeProvider.class);
        SchemaManager schemaMgr = Framework.getLocalService(SchemaManager.class);
        ((SchemaManagerImpl) schemaMgr).importTypes(typeProvider);
    }

    public synchronized void disconnect() throws Exception {
        if (locator == null) {
            throw new IllegalStateException("Client is not connected");
        }
        doDisconnect();
    }

    public synchronized void tryDisconnect() throws Exception {
        if (locator == null) {
            return; // do nothing
        }
        doDisconnect();
    }

    @Deprecated
    private void doDisconnect() throws Exception {
        locator = null;
        serverName = null;
        // close repository sessions if any
        Iterator<Entry<String, RepositoryInstance>> it = repositoryInstances.entrySet().iterator();
        while (it.hasNext()) {
            Entry<String, RepositoryInstance> repo = it.next();
            try {
                repo.getValue().close();
            } catch (Exception e) {
                log.debug("Error while trying to close " + repo, e);
            }
            it.remove();
        }
        // logout
        //        logout();
        if (!multiThreadedLogin) {
            throw new RuntimeException("This coode is dead and should never be called?"); //FIXME: REM - If you're reading this, this was left here by mistake and should be removed.
        }
        repositoryMgr = null;
        fireDisconnected(this);
    }

    public synchronized void reconnect() throws Exception {
        if (locator == null) {
            throw new IllegalStateException("Client is not connected");
        }
        InvokerLocator locator = this.locator;
        disconnect();
        connect(locator);
    }

    public AutoConfigurationService getConfigurationService() {
        return cfg;
    }

    public synchronized String getServerName() {
        if (locator == null) {
            throw new IllegalStateException("Client is not connected");
        }
        if (serverName == null) {
            if (cfg == null) { // compatibility
                serverName = RemotingService.ping(locator.getHost(), locator.getPort());
            } else {
                serverName = cfg.getServerConfiguration().getProductInfo();
            }
        }
        return serverName;
    }

    public synchronized boolean isConnected() {
        return true;
    }

    public String getServerHost() {
        if (locator == null) {
            throw new IllegalStateException("Client is not connected");
        }
        return locator.getHost();
    }

    public int getServerPort() {
        if (locator == null) {
            throw new IllegalStateException("Client is not connected");
        }
        return locator.getPort();
    }

    public InvokerLocator getLocator() {
        return locator;
    }

    public synchronized LoginHandler getLoginHandler() {
        return loginHandler;
    }

    public synchronized void setLoginHandler(LoginHandler loginHandler) {
        this.loginHandler = loginHandler;
    }

    public RepositoryManager getRepositoryManager() throws Exception {
        if (repositoryMgr == null) {
            repositoryMgr = Framework.getService(RepositoryManager.class);
        }
        return repositoryMgr;
    }

    /**
     * Gets the repositories available on the connected server.
     *
     * @return the repositories
     */
    public Repository[] getRepositories() throws Exception {
        Collection<Repository> repos = getRepositoryManager().getRepositories();
        return repos.toArray(new Repository[repos.size()]);
    }

    public Repository getDefaultRepository() throws Exception {
        return getRepositoryManager().getDefaultRepository();
    }

    public Repository getRepository(String name) throws Exception {
        return getRepositoryManager().getRepository(name);
    }

    /*
     * Open a Nuxeo repo session using the passed in repoDomain and use the default tx timeout period
     */
    public RepositoryInstance openRepository(RepositoryDomainType repoDomain) throws Exception {
        return openRepository(repoDomain.getRepositoryName(), -1);
    }

    /*
     * Open a Nuxeo repo session using the passed in repoDomain and use the default tx timeout period
     */
    public RepositoryInstance openRepository(String repoName) throws Exception {
        return openRepository(repoName, -1);
    }

    /*
     * Open a Nuxeo repo session using the default repo with the specified (passed in) tx timeout period
     */
    @Deprecated
    public RepositoryInstance openRepository(int timeoutSeconds) throws Exception {
        return openRepository(null, timeoutSeconds);
    }

    /*
     * Open a Nuxeo repo session using the default repo with the default tx timeout period
     */
    @Deprecated
    public RepositoryInstance openRepository() throws Exception {
        return openRepository(null, -1 /*default timeout period*/);
    }

    public RepositoryInstance openRepository(String repoName, int timeoutSeconds) throws Exception {
        RepositoryInstance result = null;

        if (timeoutSeconds > 0) {
            TransactionManager transactionMgr = TransactionHelper.lookupTransactionManager();
            transactionMgr.setTransactionTimeout(timeoutSeconds);
            if (logger.isInfoEnabled()) {
                logger.info(String.format("Changing current request's transaction timeout period to %d seconds",
                        timeoutSeconds));
            }
        }

        boolean startTransaction = TransactionHelper.startTransaction();
        if (startTransaction == false) {
            logger.warn("Could not start a Nuxeo transaction with the TransactionHelper class.");
        }

        Repository repository = null;
        if (repoName != null) {
            repository = getRepositoryManager().getRepository(repoName);
        } else {
            repository = getRepositoryManager().getDefaultRepository(); // Add a log info statement here stating that since no repo name was given we'll use the default repo instead
        }

        if (repository != null) {
            result = newRepositoryInstance(repository);
            String key = result.getSessionId();
            repositoryInstances.put(key, result);
            if (logger.isTraceEnabled()) {
                logger.trace("Added a new repository instance to our repo list.  Current count is now: "
                        + repositoryInstances.size());
            }
        } else {
            String errMsg = String.format("Could not open a session to the Nuxeo repository='%s'", repoName);
            logger.error(errMsg);
            throw new Exception(errMsg);
        }

        return result;
    }

    public void releaseRepository(RepositoryInstance repo) throws Exception {
        String key = repo.getSessionId();

        try {
            repo.save();
            repo.close();
        } catch (Exception e) {
            logger.error("Possible data loss.  Could not save and/or release the repository.", e);
            throw e;
        } finally {
            RepositoryInstance wasRemoved = repositoryInstances.remove(key);
            if (logger.isTraceEnabled()) {
                if (wasRemoved != null) {
                    logger.trace("Removed a repository instance from our repo list.  Current count is now: "
                            + repositoryInstances.size());
                } else {
                    logger.trace(
                            "Could not remove a repository instance from our repo list.  Current count is now: "
                                    + repositoryInstances.size());
                }
            }
            TransactionHelper.commitOrRollbackTransaction();
        }
    }

    //    public RepositoryInstance[] getRepositoryInstances() {
    //        return repositoryInstances.toArray(new RepositoryInstance[repositoryInstances.size()]);
    //    }

    public static RepositoryInstance newRepositoryInstance(Repository repository) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        if (cl == null) {
            cl = NuxeoClientEmbedded.class.getClassLoader();
        }
        return new RepositoryInstanceHandler(repository).getProxy(); // Why a proxy here?
    }

    public void addConnectionListener(ConnectionListener listener) {
        connectionListeners.add(listener);
    }

    public void removeConnectionListener(ConnectionListener listener) {
        connectionListeners.remove(listener);
    }

    private void fireDisconnected(NuxeoClientEmbedded client) {
        Object[] listeners = connectionListeners.getListeners();
        for (Object listener : listeners) {
            //            ((ConnectionListener) listener).disconnected(client);
        }
    }

    private void fireConnected(NuxeoClientEmbedded client) {
        Object[] listeners = connectionListeners.getListeners();
        for (Object listener : listeners) {
            //            ((ConnectionListener) listener).connected(client);
        }
    }
}