org.mule.providers.jcr.JcrConnector.java Source code

Java tutorial

Introduction

Here is the source code for org.mule.providers.jcr.JcrConnector.java

Source

/*
 * $Id$
 * --------------------------------------------------------------------------------------
 * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
 *
 * The software in this package is published under the terms of the MuleSource MPL
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */

package org.mule.providers.jcr;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.jcr.Credentials;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;

import org.apache.commons.lang.UnhandledException;
import org.mule.config.i18n.CoreMessages;
import org.mule.impl.MuleEvent;
import org.mule.impl.MuleMessage;
import org.mule.impl.RequestContext;
import org.mule.impl.model.streaming.CallbackOutputStream;
import org.mule.providers.AbstractConnector;
import org.mule.providers.FatalConnectException;
import org.mule.providers.jcr.handlers.NodeTypeHandler;
import org.mule.providers.jcr.handlers.NodeTypeHandlerManager;
import org.mule.providers.jcr.i18n.JcrMessages;
import org.mule.umo.UMOEvent;
import org.mule.umo.UMOException;
import org.mule.umo.UMOMessage;
import org.mule.umo.UMOSession;
import org.mule.umo.endpoint.UMOImmutableEndpoint;
import org.mule.umo.lifecycle.InitialisationException;
import org.mule.umo.provider.ConnectorException;
import org.mule.umo.provider.DispatchException;
import org.mule.util.ClassUtils;

/**
 * <code>JcrConnector</code> is a transport that connects to JCR 1.0 (aka JSR
 * 170) repositories.
 * 
 * @author David Dossot (david@dossot.net)
 */
public final class JcrConnector extends AbstractConnector {

    private Repository repository;

    private String workspaceName;

    private String username;

    private String password;

    private Integer eventTypes;

    private Boolean deep;

    private List uuid;

    private List nodeTypeName;

    private Boolean noLocal;

    private String contentPayloadType;

    private final NodeTypeHandlerManager nodeTypeHandlerManager;

    /**
     * Property that defines a relative path to append at the end of the target
     * item path.
     */
    public static final String JCR_PROPERTY_REL_PATH_PROPERTY = "propertyRelPath";

    /**
     * Property that defines a relative path to append after the endpoint item
     * path.
     */
    public static final String JCR_NODE_RELPATH_PROPERTY = "nodeRelPath";

    /**
     * Property that forces the creation of a child node under the node target
     * by the endpoint URI, instead of trying first to locate an existing one.
     */
    public static final String JCR_ALWAYS_CREATE_CHILD_NODE_PROPERTY = "alwaysCreate";

    /**
     * Property that defines a particular node type name.
     */
    public static final String JCR_NODE_TYPE_NAME_PROPERTY = "nodeTypeName";

    /**
     * Property that forces the lookup of a particular node by UUID.
     */
    public static final String JCR_NODE_UUID_PROPERTY = "nodeUUID";

    /**
     * Property that defines a repository search query statement.
     */
    public static final String JCR_QUERY_STATEMENT_PROPERTY = "queryStatement";

    /**
     * Property that defines a repository search query language.
     */
    public static final String JCR_QUERY_LANGUAGE_PROPERTY = "queryLanguage";

    public JcrConnector() {
        super();

        setDefaultEndpointValues();

        nodeTypeHandlerManager = new NodeTypeHandlerManager();
    }

    public void doInitialise() throws InitialisationException {
        if (getRepository() == null) {
            throw new InitialisationException(JcrMessages.missingDependency("repository"), this);
        }

        if (getRepository().getDescriptor(Repository.OPTION_QUERY_SQL_SUPPORTED) == null) {
            logger.info(JcrMessages.sqlQuerySyntaxNotSupported());
        }

    }

    public void doConnect() throws Exception {
        // NOOP
    }

    public void doStart() throws org.mule.umo.UMOException {
        // NOOP
    }

    public void doStop() throws org.mule.umo.UMOException {
        // NOOP
    }

    public void doDisconnect() throws Exception {
        // NOOP
    }

    public void doDispose() {
        // NOOP
    }

    /**
     * Will get the output stream for this type of transport. Typically this
     * will be called only when Streaming is being used on an outbound endpoint.
     */
    public OutputStream getOutputStream(final UMOImmutableEndpoint endpoint, UMOMessage message)
            throws UMOException {

        final PipedInputStream pipedInputStream = new PipedInputStream();
        PipedOutputStream pipedOutputStream;

        try {
            pipedOutputStream = new PipedOutputStream(pipedInputStream);
        } catch (IOException ioe) {
            throw new ConnectorException(CoreMessages.streamingFailedForEndpoint(endpoint.toString()), this, ioe);
        }

        final Map properties = new HashMap();

        for (Iterator i = message.getPropertyNames().iterator(); i.hasNext();) {
            String propertyName = (String) i.next();
            properties.put(propertyName, message.getProperty(propertyName));
        }

        final UMOEvent event = RequestContext.getEvent();
        final UMOSession session = event != null ? event.getSession() : null;

        // It is essential to use a different thread for reading the piped input
        // stream. Doing a dispatch and relying on Mule's work manager to do so
        // might be a better option but this would require to force threading
        // and never execute in the same thread even if the pool is expired.
        final Thread thread = new Thread(new Runnable() {
            public void run() {
                try {
                    send(endpoint,
                            new MuleEvent(new MuleMessage(pipedInputStream, properties), endpoint, session, true));
                } catch (DispatchException de) {
                    logger.error("Can not send streaming message!", de);
                }
            }
        });

        thread.start();

        return new CallbackOutputStream(pipedOutputStream, new JoinThreadCallback(thread));
    }

    private static final class JoinThreadCallback implements CallbackOutputStream.Callback {

        private final Thread thread;

        JoinThreadCallback(Thread thread) {
            this.thread = thread;
        }

        public void onClose() throws Exception {
            thread.join();
        }

    }

    public Session newSession() throws RepositoryException {
        if (logger.isDebugEnabled()) {
            logger.debug("Opening new JCR session.");
        }

        Credentials credentials = ((getUsername() != null) && (getPassword() != null))
                ? new SimpleCredentials(getUsername(), getPassword().toCharArray())
                : null;

        return getRepository().login(credentials, getWorkspaceName());
    }

    public void terminateSession(Session session) {
        if ((session != null) && (session.isLive())) {
            if (logger.isDebugEnabled()) {
                logger.debug("Terminating JCR session");
            }

            session.logout();
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("Can not terminate session: " + session);
            }
        }
    }

    public Session validateSession(Session session) {
        if ((session != null) && (session.isLive())) {
            return session;
        } else {
            logger.info("JCR session is invalid: a new one will be created.");

            try {
                return newSession();
            } catch (RepositoryException re) {
                logger.error("Impossible to reconnect to the JCR container!", re);

                if (connectionStrategy != null) {
                    initialised.set(false);

                    try {
                        stopConnector();
                    } catch (UMOException umoe) {
                        logger.warn(umoe.getMessage(), umoe);
                    }

                    try {
                        connectionStrategy.connect(this);
                        initialise();
                        startConnector();
                        return session;

                    } catch (FatalConnectException fce) {
                        throw new IllegalStateException("Failed to reconnect to JCR server. I'm giving up.");
                    } catch (UMOException umoe) {
                        throw new UnhandledException("Failed to recover a connector.", umoe);
                    }

                } else {
                    throw new IllegalStateException("Connection to the JCR container has been lost "
                            + "and no connection strategy has been defined on the connector!");
                }
            }
        }
    }

    private void setDefaultEndpointValues() {
        // any change here must be reflected in the documentation
        setContentPayloadType(JcrContentPayloadType.NONE.toString());
        setEventTypes(new Integer(0));
        setDeep(Boolean.FALSE);
        setNoLocal(Boolean.TRUE);
        setUuid(null);
        setNodeTypeName(null);
    }

    public String getProtocol() {
        return "jcr";
    }

    /**
     * Sets an optional list of <code>NodeTypeHandlers</code> class names.
     * 
     * @param customNodeTypeHandlers
     */
    public void setCustomNodeTypeHandlers(List customNodeTypeHandlers) {
        if (customNodeTypeHandlers != null) {
            for (int i = 0; i < customNodeTypeHandlers.size(); i++) {
                String customNodeTypeHandlerClassName = customNodeTypeHandlers.get(i).toString();

                try {
                    NodeTypeHandler handler = (NodeTypeHandler) ClassUtils
                            .instanciateClass(customNodeTypeHandlerClassName, ClassUtils.NO_ARGS, this.getClass());

                    getNodeTypeHandlerManager().registerHandler(handler);
                } catch (Exception e) {
                    logger.error("Can not load custom type handler: " + customNodeTypeHandlerClassName, e);
                }
            }
        }
    }

    /**
     * @return the nodeTypeHandlerManager
     */
    public NodeTypeHandlerManager getNodeTypeHandlerManager() {
        return nodeTypeHandlerManager;
    }

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

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

    /**
     * @return the repository
     */
    public Repository getRepository() {
        return repository;
    }

    /**
     * @param repository
     *            the repository to set
     */
    public void setRepository(Repository repository) {
        this.repository = repository;
    }

    /**
     * @return the username
     */
    public String getUsername() {
        return username;
    }

    /**
     * @param username
     *            the username to set
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * @return the workspaceName
     */
    public String getWorkspaceName() {
        return workspaceName;
    }

    /**
     * @param workspaceName
     *            the workspaceName to set
     */
    public void setWorkspaceName(String workspaceName) {
        this.workspaceName = workspaceName;
    }

    /**
     * @return the deep
     */
    public Boolean isDeep() {
        return deep;
    }

    /**
     * @param deep
     *            the deep to set
     */
    public void setDeep(Boolean deep) {
        this.deep = deep;
    }

    /**
     * @return the eventTypes
     */
    public Integer getEventTypes() {
        return eventTypes;
    }

    /**
     * @param eventTypes
     *            the eventTypes to set
     */
    public void setEventTypes(Integer eventTypes) {
        this.eventTypes = eventTypes;
    }

    /**
     * @return the nodeTypeName
     */
    public List getNodeTypeName() {
        return nodeTypeName;
    }

    /**
     * @param nodeTypeName
     *            the nodeTypeName to set
     */
    public void setNodeTypeName(List nodeTypeName) {
        this.nodeTypeName = nodeTypeName;
    }

    /**
     * @return the noLocal
     */
    public Boolean isNoLocal() {
        return noLocal;
    }

    /**
     * @param noLocal
     *            the noLocal to set
     */
    public void setNoLocal(Boolean noLocal) {
        this.noLocal = noLocal;
    }

    /**
     * @return the uuid
     */
    public List getUuid() {
        return uuid;
    }

    /**
     * @param uuid
     *            the uuid to set
     */
    public void setUuid(List uuid) {
        this.uuid = uuid;
    }

    /**
     * @return the contentPayloadType
     */
    public String getContentPayloadType() {
        return contentPayloadType;
    }

    /**
     * @param contentPayloadType
     *            the contentPayloadType to set
     */
    public void setContentPayloadType(String contentPayloadType) {
        this.contentPayloadType = contentPayloadType;
    }

}