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

org.efaps.db.store.JCRStoreResource Maven / Gradle / Ivy

/*
 * Copyright 2003 - 2013 The eFaps Team
 *
 * 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.
 *
 * Revision:        $Rev: 8848 $
 * Last Changed:    $Date: 2013-02-19 10:49:59 -0500 (Tue, 19 Feb 2013) $
 * Last Changed By: $Author: [email protected] $
 */

package org.efaps.db.store;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Calendar;

import javax.jcr.AccessDeniedException;
import javax.jcr.Binary;
import javax.jcr.InvalidItemStateException;
import javax.jcr.ItemExistsException;
import javax.jcr.LoginException;
import javax.jcr.NoSuchWorkspaceException;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.ReferentialIntegrityException;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.nodetype.NodeType;
import javax.jcr.version.VersionException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.transaction.xa.XAException;
import javax.transaction.xa.Xid;

import org.efaps.db.Context;
import org.efaps.db.Instance;
import org.efaps.db.transaction.ConnectionResource;
import org.efaps.db.wrapper.SQLSelect;
import org.efaps.util.EFapsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Store Resource that uses the Content Repository for Java Technology API (JCR).
 *
 * @author The eFaps Team
 * @version $Id: JCRStoreResource.java 8848 2013-02-19 15:49:59Z [email protected] $
 */
public class JCRStoreResource
    extends AbstractStoreResource
{
    /**
     * Name of the table the content is stored in.
     */
    public static final String TABLENAME_STORE = "T_CMGENSTOREJCR";

    /**
     * Name of the column the content is stored in.
     */
    public static final String COLNAME_IDENTIFIER = "IDENTIFIER";

    /**
     * Logging instance used in this class.
     */
    private static final Logger LOG  = LoggerFactory.getLogger(JCRStoreResource.class);

    /**
     * Property Name to define if the type if is used to define a sub
     * directory.
     */
    private static final String PROPERTY_WORKSPACENAME = "JCRWorkSpaceName";

    /**
     * Property Name to define if the file will be deleted on deletion of the related object.
     */
    private static final String PROPERTY_ENABLEDELETION = "JCREnableDeletion";

    /**
     * Property Name to define if the file will be deleted on deletion of the related object.
     */
    private static final String PROPERTY_USERNAME = "JCRUserName";

    /**
     * Property Name to define if the file will be deleted on deletion of the related object.
     */
    private static final String PROPERTY_PASSWORD = "JCRPassword";


    /**
     * The repository for this JCR Store Resource.
     */
    private Repository repository;

    /**
     * Identifier for the node to be accessed.
     */
    private String identifier;

    /**
     * Session for JCR access.
     */
    private Session session;

    /**
     * {@inheritDoc}
     */
    @Override
    public void initialize(final Instance _instance,
                           final Store _store)
        throws EFapsException
    {
        super.initialize(_instance, _store);
        try {
            final InitialContext ctx = new InitialContext();
            this.repository = (Repository) ctx.lookup(getStore().getProperty(Store.PROPERTY_JNDINAME));
            if (JCRStoreResource.LOG.isDebugEnabled()) {
                final String name = this.repository.getDescriptor(Repository.REP_NAME_DESC);
                JCRStoreResource.LOG.debug("Successfully retrieved '%s' repository from JNDI", new Object[]{ name });
            }
            String username = getProperties().get(JCRStoreResource.PROPERTY_USERNAME);
            if (username == null) {
                username = Context.getThreadContext().getPerson().getName();
            }
            String passwd = getProperties().get(JCRStoreResource.PROPERTY_PASSWORD);
            if (passwd == null) {
                passwd = "efaps";
            }
            this.session = this.repository.login(new SimpleCredentials(username, passwd.toCharArray()),
                            getProperties().get(JCRStoreResource.PROPERTY_WORKSPACENAME));
        } catch (final NamingException e) {
            throw new EFapsException(JCRStoreResource.class, "initialize.NamingException", e);
        } catch (final LoginException e) {
            throw new EFapsException(JCRStoreResource.class, "initialize.LoginException", e);
        } catch (final NoSuchWorkspaceException e) {
            throw new EFapsException(JCRStoreResource.class, "initialize.NoSuchWorkspaceException", e);
        } catch (final RepositoryException e) {
            throw new EFapsException(JCRStoreResource.class, "initialize.RepositoryException", e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected int add2Select(final SQLSelect _select)
    {
        _select.column(2, "ID").column(2, JCRStoreResource.COLNAME_IDENTIFIER)
            .leftJoin(JCRStoreResource.TABLENAME_STORE, 2, "ID", 0, "ID");
        return 1;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void getAdditionalInfo(final ResultSet _rs)
        throws SQLException
    {
        final String identiferTmp = _rs.getString(6);
        if (identiferTmp != null && !identiferTmp.isEmpty()) {
            this.identifier = identiferTmp.trim();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void insertDefaults()
        throws EFapsException
    {
        super.insertDefaults();
        if (!getExist()[1] && getGeneralID() != null) {
            try {
                final ConnectionResource res = Context.getThreadContext().getConnectionResource();
                final Connection con = res.getConnection();
                Context.getDbType().newInsert(JCRStoreResource.TABLENAME_STORE, "ID", false)
                                .column("ID", getGeneralID())
                                .column(JCRStoreResource.COLNAME_IDENTIFIER, "NEW")
                                .execute(con);
                res.commit();
            } catch (final SQLException e) {
                throw new EFapsException(JCRStoreResource.class, "insertDefaults", e);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public long write(final InputStream _in,
                     final long _size,
                     final String _fileName)
        throws EFapsException
    {
        final JCRBinary bin = new JCRBinary(_in);
        long size = _size;
        try {
            final Node rootNode = this.session.getRootNode();
            final Node fileNode = rootNode.addNode(getInstance().getOid(), NodeType.NT_FILE);
            final Node resNode = fileNode.addNode(Property.JCR_CONTENT, NodeType.NT_RESOURCE);
            resNode.setProperty(Property.JCR_DATA, bin);
            resNode.setProperty(Property.JCR_LAST_MODIFIED, Calendar.getInstance());
            resNode.setProperty(Property.JCR_LAST_MODIFIED_BY, Context.getThreadContext().getPerson().getName());
            setIdentifer(fileNode.getIdentifier());
            // if size is unkown!
            if (size < 0)  {
                final byte[] buffer = new byte[1024];
                int length = 1;
                size = 0;
                final OutputStream out = new ByteArrayOutputStream();
                while (length > 0)  {
                    length = _in.read(buffer);
                    if (length > 0)  {
                        out.write(buffer, 0, length);
                        size += length;
                    }
                }
            }
        } catch (final RepositoryException e) {
            throw new EFapsException(JCRStoreResource.class, "write.RepositoryException", e);
        } catch (final IOException e) {
            throw new EFapsException(JCRStoreResource.class, "write.IOException", e);
        }
        setFileInfo(_fileName, size);
        return size;
    }

    /**
     * Set the identifier in the eFaps DataBase.
     * @param _identifier   identifer to set
     * @throws EFapsException on error
     */
    protected void setIdentifer(final String _identifier)
        throws EFapsException
    {
        if (!_identifier.equals(this.identifier)) {

            ConnectionResource res = null;
            try {
                res = Context.getThreadContext().getConnectionResource();

                final StringBuffer cmd = new StringBuffer().append("update ")
                                .append(JCRStoreResource.TABLENAME_STORE).append(" set ")
                                .append(JCRStoreResource.COLNAME_IDENTIFIER).append("=? ")
                                .append("where ID =").append(getGeneralID());

                final PreparedStatement stmt = res.getConnection().prepareStatement(cmd.toString());
                try {
                    stmt.setString(1, _identifier);
                    stmt.execute();
                } finally {
                    stmt.close();
                }
                res.commit();
                this.identifier = _identifier;
            } catch (final EFapsException e) {
                res.abort();
                throw e;
            } catch (final SQLException e) {
                res.abort();
                throw new EFapsException(JDBCStoreResource.class, "write.SQLException", e);
            }
        }
    }

    /**
     * A JCR Store resource does not use compression from eFaps Side.
     * @return Compress.NONE
     */
    @Override
    protected Compress getCompress()
    {
        return Compress.NONE;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public InputStream read()
        throws EFapsException
    {
        InputStream input = null;
        try {
            final Node fileNode = this.session.getNodeByIdentifier(this.identifier);
            final Node resNode = fileNode.getNode(Property.JCR_CONTENT);
            final Property data = resNode.getProperty(Property.JCR_DATA);
            final Binary bin = data.getBinary();
            input = new JCRStoreResourceInputStream(this, bin);
        } catch (final RepositoryException e) {
            throw new EFapsException(JCRStoreResource.class, "read.RepositoryException", e);
        } catch (final IOException e) {
            throw new EFapsException(JCRStoreResource.class, "read.IOException", e);
        }
        return input;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void delete()
        throws EFapsException
    {
        // only delete if it actually exists and deletion is configured
        if (getExist()[0] && getExist()[1]
                        && "TRUE".equalsIgnoreCase(getProperties().get(JCRStoreResource.PROPERTY_ENABLEDELETION))) {
            try {
                final Node fileNode = this.session.getNodeByIdentifier(this.identifier);
                fileNode.remove();
            } catch (final RepositoryException e) {
                throw new EFapsException(JCRStoreResource.class, "delete.RepositoryException", e);
            }
        }
    }

    /**
     * The method is called from the transaction manager if the complete
     * transaction is completed.
* * @param _xid global transaction identifier (not used, because each * file with the file id gets a new VFS store resource * instance) * @param _onePhase true if it is a one phase commitment transaction * (not used) * @throws XAException if any exception occurs (catch on * {@link java.lang.Throwable}) */ @Override public void commit(final Xid _xid, final boolean _onePhase) throws XAException { try { if (this.session.hasPendingChanges()) { this.session.save(); } this.session.logout(); } catch (final AccessDeniedException e) { throw new XAException("AccessDeniedException"); } catch (final ItemExistsException e) { throw new XAException("ItemExistsException"); } catch (final ReferentialIntegrityException e) { throw new XAException("ReferentialIntegrityException"); } catch (final ConstraintViolationException e) { throw new XAException("AccessDeniedException"); } catch (final InvalidItemStateException e) { throw new XAException("InvalidItemStateException"); } catch (final VersionException e) { throw new XAException("VersionException"); } catch (final LockException e) { throw new XAException(XAException.XA_RBDEADLOCK); } catch (final NoSuchNodeTypeException e) { throw new XAException("NoSuchNodeTypeException"); } catch (final RepositoryException e) { throw new XAException("RepositoryException"); } } /** * Tells the resource manager to forget about a heuristically completed * transaction branch. * * @param _xid global transaction identifier (not used, because each file * with the file id gets a new VFS store resource instance) */ @Override public void forget(final Xid _xid) { if (JCRStoreResource.LOG.isDebugEnabled()) { JCRStoreResource.LOG.debug("forget (xid = " + _xid + ")"); } } /** * Obtains the current transaction timeout value set for this XAResource * instance. * * @return always 0 */ public int getTransactionTimeout() { if (JCRStoreResource.LOG.isDebugEnabled()) { JCRStoreResource.LOG.debug("getTransactionTimeout"); } return 0; } /** * Ask the resource manager to prepare for a transaction commit of the * transaction specified in xid (used for 2-phase commits). * * @param _xid Xid * @return always 0, because not 2 phase commit */ public int prepare(final Xid _xid) { if (JCRStoreResource.LOG.isDebugEnabled()) { JCRStoreResource.LOG.debug("prepare (xid=" + _xid + ")"); } return 0; } /** * Obtains a list of prepared transaction branches from a resource manager. * * @param _flag flag * @return always null */ public Xid[] recover(final int _flag) { if (JCRStoreResource.LOG.isDebugEnabled()) { JCRStoreResource.LOG.debug("recover (flag = " + _flag + ")"); } return null; } /** * On rollback no save is send to the session.. * * @param _xid global transaction identifier (not used, because each * file with the file id gets a new VFS store resource * instance) * @throws XAException if any exception occurs (catch on * {@link java.lang.Throwable}) */ @Override public void rollback(final Xid _xid) throws XAException { this.session.logout(); } /** * Sets the current transaction timeout value for this XAResource instance. * * @param _seconds number of seconds * @return always true */ public boolean setTransactionTimeout(final int _seconds) { if (JCRStoreResource.LOG.isDebugEnabled()) { JCRStoreResource.LOG.debug("setTransactionTimeout (seconds = " + _seconds + ")"); } return true; } /** * Implementation of Binary from JCR. */ private static class JCRBinary implements Binary { /** * The InpuStrema this Binary belongs to. */ private InputStream stream; /** * @param _in InputStream */ public JCRBinary(final InputStream _in) { this.stream = _in; } /** * Returns an {@link InputStream} representation of this value. Each call to * getStream() returns a new stream. The API consumer is * responsible for calling close() on the returned stream. *

* If {@link #dispose()} has been called on this Binary * object, then this method will throw the runtime exception * {@link java.lang.IllegalStateException}. * * @return A stream representation of this value. * @throws RepositoryException if an error occurs. */ @Override public InputStream getStream() throws RepositoryException { return this.stream; } /** * Reads successive bytes from the specified position in this * Binary into the passed byte array until either the byte * array is full or the end of the Binary is encountered. *

* If {@link #dispose()} has been called on this Binary * object, then this method will throw the runtime exception * {@link java.lang.IllegalStateException}. * * @param _b the buffer into which the data is read. * @param _position the position in this Binary from which to start reading * bytes. * @return the number of bytes read into the buffer, or -1 if there is no * more data because the end of the Binary has been reached. * @throws IOException if an I/O error occurs. * @throws NullPointerException if b is null. * @throws IllegalArgumentException if offset is negative. * @throws RepositoryException if another error occurs. */ @Override public int read(final byte[] _b, final long _position) throws IOException, RepositoryException { return this.stream.read(_b); } /** * Returns the size of this Binary value in bytes. *

* If {@link #dispose()} has been called on this Binary * object, then this method will throw the runtime exception * {@link java.lang.IllegalStateException}. * * @return the size of this value in bytes. * @throws RepositoryException if an error occurs. */ @Override public long getSize() throws RepositoryException { return 0; } /** * Releases all resources associated with this Binary object * and informs the repository that these resources may now be reclaimed. * An application should call this method when it is finished with the * Binary object. */ @Override public void dispose() { try { this.stream.close(); } catch (final IOException e) { JCRStoreResource.LOG.error("Error on disposal of inpustream.", e); } } } /** * ResourceInputStream implementation. */ private class JCRStoreResourceInputStream extends StoreResourceInputStream { /** * @param _store Strore this InputStream belongs to * @param _bin Binary * @throws IOException on error * @throws RepositoryException on error */ protected JCRStoreResourceInputStream(final AbstractStoreResource _store, final Binary _bin) throws IOException, RepositoryException { super(_store, _bin.getStream()); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy