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

org.efaps.db.store.AbstractStoreResource 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.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.sql.Statement;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipInputStream;

import org.efaps.db.Context;
import org.efaps.db.GeneralInstance;
import org.efaps.db.Instance;
import org.efaps.db.InstanceQuery;
import org.efaps.db.databases.AbstractDatabase;
import org.efaps.db.transaction.AbstractResource;
import org.efaps.db.transaction.ConnectionResource;
import org.efaps.db.wrapper.SQLPart;
import org.efaps.db.wrapper.SQLSelect;
import org.efaps.util.EFapsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author The eFaps Team
 * @version $Id: AbstractStoreResource.java 8848 2013-02-19 15:49:59Z [email protected] $
 */
public abstract class AbstractStoreResource
    extends AbstractResource
    implements Resource
{
    /**
     * Name of the main store table.
     */
    public static final String TABLENAME_STORE = "T_CMGENSTORE";

    /**
     * Name of the column for the filename.
     */
    public static final String COLNAME_FILENAME = "FILENAME";

    /**
     * Name of the column for the file length.
     */
    public static final String COLNAME_FILELENGTH = "FILELENGTH";

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

    /**
     * Basic SQL Select for getting the Resource.
     */
    private static final SQLSelect SQL_SELECT = new SQLSelect()
                                                    .column(0, "ID")
                                                    .column(1, AbstractStoreResource.COLNAME_FILENAME)
                                                    .column(1, AbstractStoreResource.COLNAME_FILELENGTH)
                                                    .column(1, "ID")
                                                    .from(GeneralInstance.TABLENAME, 0)
                                                    .leftJoin(AbstractStoreResource.TABLENAME_STORE, 1, "ID", 0, "ID");

    /**
     * @see #StoreEvent
     */
    private StoreEvent storeEvent = StoreEvent.UNKNOWN;

    /**
     * Buffer used to copy from the input stream to the output stream.
     *
     * @see #read()
     */
    private final byte[] buffer = new byte[1024];

    /**
     * Instance this resource belongs to.
     */
    private Instance instance;

    /**
     * GeneralID of this Store Resource.
     */
    private Long generalID;

    /**
     * Do the related objects exist.
     */
    private boolean[] exist;

    /**
     * File Name of the Source.
     */
    private String fileName = "DEFAULT";

    /**
     * Length of the file in byte.
     */
    private Long fileLength = new Long(0);

    /**
     * Store this Resource belongs to.
     */
    private Store store;

    /**
     * {@inheritDoc}
     */
    @Override
    public void initialize(final Instance _instance,
                           final Store _store)
        throws EFapsException
    {
        this.instance = _instance;
        this.store = _store;
        final SQLSelect select = AbstractStoreResource.SQL_SELECT.getCopy()
                        .addPart(SQLPart.WHERE)
                        .addColumnPart(0, "INSTTYPEID").addPart(SQLPart.EQUAL)
                        .addValuePart(_instance.getType().getId())
                        .addPart(SQLPart.AND)
                        .addColumnPart(0, "INSTID").addPart(SQLPart.EQUAL).addValuePart(_instance.getId());
        this.exist = new boolean[1 + add2Select(select)];
        getGeneralID(select.getSQL());

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void open(final StoreEvent _event)
        throws EFapsException
    {
        this.storeEvent = _event;
        super.open();
        if (getStoreEvent().equals(StoreEvent.READ) || getStoreEvent().equals(StoreEvent.WRITE)) {
            insertDefaults();
        }
    }

    /**
     * The output stream is written with the content of the file. From method {@link #read()} the input stream is used
     * and copied into the output stream.
     *
     * @param _out output stream where the file content must be written
     * @throws EFapsException if an error occurs
     * @see #read()
     */
    public void read(final OutputStream _out)
        throws EFapsException
    {
        StoreResourceInputStream in = null;
        try {
            in = (StoreResourceInputStream) read();
            if (in != null) {
                int length = 1;
                while (length > 0) {
                    length = in.read(this.buffer);
                    if (length > 0) {
                        _out.write(this.buffer, 0, length);
                    }
                }
            }
        } catch (final IOException e) {
            throw new EFapsException(AbstractStoreResource.class, "read.IOException", e);
        } finally {
            if (in != null) {
                try {
                    in.closeWithoutCommit();
                } catch (final IOException e) {
                    AbstractStoreResource.LOG.warn("Catched IOException in class: " + this.getClass());
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getFileName()
        throws EFapsException
    {
        return this.fileName;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Long getFileLength()
        throws EFapsException
    {
        return this.fileLength;
    }

    /**
     * Insert default values in the table. (if necessary).
     * @throws EFapsException on error
     */
    protected void insertDefaults()
        throws EFapsException
    {
        if (!getExist()[0] && getGeneralID() != null) {
            try {
                final ConnectionResource res = Context.getThreadContext().getConnectionResource();
                final Connection con = res.getConnection();
                Context.getDbType().newInsert(AbstractStoreResource.TABLENAME_STORE, "ID", false)
                                .column("ID", getGeneralID())
                                .column(AbstractStoreResource.COLNAME_FILENAME, "TMP")
                                .column(AbstractStoreResource.COLNAME_FILELENGTH, 0)
                                .execute(con);
                res.commit();
                this.fileName = "TMP";
                this.fileLength = new Long(0);
            } catch (final SQLException e) {
                throw new EFapsException(AbstractStoreResource.class, "insertDefaults", e);
            }
        }
    }


    /**
     * Set the info for the file in this store reosurce.
     * @param _filename     name of the file
     * @param _fileLength   length of the file
     * @throws EFapsException on error
     */
    protected void setFileInfo(final String _filename,
                               final long _fileLength)
        throws EFapsException
    {
        if (!_filename.equals(this.fileName) || _fileLength != this.fileLength) {
            ConnectionResource res = null;
            try {
                res = Context.getThreadContext().getConnectionResource();
                final AbstractDatabase db = Context.getDbType();
                final StringBuilder cmd = new StringBuilder().append(db.getSQLPart(SQLPart.UPDATE)).append(" ")
                        .append(db.getTableQuote()).append(AbstractStoreResource.TABLENAME_STORE)
                        .append(db.getTableQuote())
                        .append(" ").append(db.getSQLPart(SQLPart.SET)).append(" ")
                        .append(db.getColumnQuote())
                        .append(AbstractStoreResource.COLNAME_FILENAME)
                        .append(db.getColumnQuote()).append(db.getSQLPart(SQLPart.EQUAL)).append("? ")
                        .append(db.getSQLPart(SQLPart.COMMA))
                        .append(db.getColumnQuote())
                        .append(AbstractStoreResource.COLNAME_FILELENGTH)
                        .append(db.getColumnQuote()).append(db.getSQLPart(SQLPart.EQUAL)).append("? ")
                        .append(db.getSQLPart(SQLPart.WHERE)).append(" ")
                        .append(db.getColumnQuote()).append("ID").append(db.getColumnQuote())
                        .append(db.getSQLPart(SQLPart.EQUAL)).append(getGeneralID());

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

    /**
     * Add to the select for the existence check.
     * @param _select select to add to
     * @return number of added columns
     */
    protected abstract int add2Select(final SQLSelect _select);

    /**
     * Get the generalID etc. from the eFasp DataBase.
     * @param _complStmt Statement to be executed
     * @throws EFapsException on error
     */
    private void getGeneralID(final String _complStmt)
        throws EFapsException
    {
        ConnectionResource con = null;
        try {
            con = Context.getThreadContext().getConnectionResource();

            final Statement stmt = con.getConnection().createStatement();

            final ResultSet rs = stmt.executeQuery(_complStmt.toString());

            while (rs.next()) {
                this.generalID = rs.getLong(1);
                this.fileName = rs.getString(2);
                if (this.fileName != null && !this.fileName.isEmpty()) {
                    this.fileName = this.fileName.trim();
                }
                this.fileLength = rs.getLong(3);
                for (int i = 0; i < this.exist.length; i++) {
                    this.exist[i] = rs.getLong(4 + i) > 0;
                }
                getAdditionalInfo(rs);
            }
            rs.close();
            stmt.close();
            con.commit();
        } catch (final SQLException e) {
            throw new EFapsException(InstanceQuery.class, "executeOneCompleteStmt", e);
        } finally {
            if (con != null && con.isOpened()) {
                con.abort();
            }
        }
    }

    /**
     * Can be used by implementation to get additionla information form the database.
     * @param _rs   ResultSet
     * @throws SQLException on error
     */
    protected void getAdditionalInfo(final ResultSet _rs)
        throws SQLException
    {
    }

    /**
     * Frees this resource. Only a dummy implementation because nothing must be freed for this store.
     */
    @Override
    protected void freeResource()
    {
    }

    /**
     * Getter method for instance variable {@link #instance}.
     *
     * @return value of instance variable {@link #instance}
     */
    protected Instance getInstance()
    {
        return this.instance;
    }

    /**
     * Setter method for instance variable {@link #instance}.
     *
     * @param _instance value for instance variable {@link #instance}
     */
    protected void setInstance(final Instance _instance)
    {
        this.instance = _instance;
    }

    /**
     * Is this Store resource compressed.
     *
     * @return Is this Store resource compressed
     */
    protected Compress getCompress()
    {
        Compress compress;
        if (this.store.getResourceProperties().containsKey(Store.PROPERTY_COMPRESS)) {
            compress = Compress.valueOf(this.store.getResourceProperties().get(Store.PROPERTY_COMPRESS).toUpperCase());
        } else {
            compress = Compress.NONE;
        }
        return compress;
    }

    /**
     * Getter method for instance variable {@link #store}.
     *
     * @return value of instance variable {@link #store}
     */
    protected Store getStore()
    {
        return this.store;
    }

    /**
     * Get the properties for this resource.
     *
     * @return properties for this resource
     */
    protected Map getProperties()
    {
        return this.store.getResourceProperties();
    }

    /**
     * Getter method for instance variable {@link #exist}.
     *
     * @return value of instance variable {@link #exist}
     */
    protected boolean[] getExist()
    {
        return this.exist;
    }

    /**
     * Getter method for instance variable {@link #generalID}.
     *
     * @return value of instance variable {@link #generalID}
     */
    protected Long getGeneralID()
    {
        return this.generalID;
    }

    /**
     * Getter method for instance variable {@link #storeEvent}.
     *
     * @return value of instance variable {@link #storeEvent}
     */
    protected StoreEvent getStoreEvent()
    {
        return this.storeEvent;
    }

    /**
     * Wraps the standard {@link InputStream} to get an input stream for the needs of eFaps.
     */
    protected class StoreResourceInputStream
        extends InputStream
    {
        /**
         * InputStream.
         */
        private final InputStream in;

        /**
         * StoreResource for this InputStream.
         */
        private final AbstractStoreResource store;

        /**
         * @param _resource StoreResource this InputStream belong to
         * @param _in       inputstream
         * @throws IOException on error
         */
        protected StoreResourceInputStream(final AbstractStoreResource _resource,
                                           final InputStream _in)
            throws IOException
        {
            this.store = _resource;
            if (_resource.getCompress().equals(Compress.GZIP)) {
                this.in = new GZIPInputStream(_in);
            } else if (_resource.getCompress().equals(Compress.ZIP)) {
                this.in = new ZipInputStream(_in);
            } else {
                this.in = _in;
            }
        }

        /**
         * The input stream itself is closed.
         *
         * @throws IOException on error
         */
        protected void beforeClose()
            throws IOException
        {
            this.in.close();
        }

        /**
         * Only a dummy method if something must happened after the commit of the store.
         *
         * @throws IOException on error
         */
        protected void afterClose()
            throws IOException
        {
        }

        /**
         * The input stream and others are closes without commit of the store resource.
         *
         * @throws IOException on error
         */
        private void closeWithoutCommit()
            throws IOException
        {
            beforeClose();
            afterClose();
        }

        /**
         * Calls method {@link #beforeClose()}, then commits the store and at least calls method {@link #afterClose()}.
         *
         * @see #beforeClose()
         * @see #afterClose()
         * @throws IOException on error
         */
        @Override
        public void close()
            throws IOException
        {
            try {
                super.close();
                beforeClose();
                if (this.store.isOpened()) {
                    this.store.commit();
                }
                afterClose();
            } catch (final EFapsException e) {
                throw new IOException("commit of store not possible", e);
            } finally {
                if (this.store.isOpened()) {
                    try {
                        this.store.abort();
                    } catch (final EFapsException e) {
                        throw new IOException("store resource could not be aborted", e);
                    }
                }
            }
        }

        /**
         * @return 0 if available, else 1
         * @throws IOException on error
         * @see InputStream#available()
         */
        @Override
        public int available()
            throws IOException
        {
            return this.in.available();
        }

        /**
         * @param _readlimit limit to read
         * @see InputStream#mark(int)
         */
        @Override
        public void mark(final int _readlimit)
        {
            this.in.mark(_readlimit);
        }

        /**
         * @return mark suported
         * @see InputStream#markSupported()
         */
        @Override
        public boolean markSupported()
        {
            return this.in.markSupported();
        }

        /**
         * @return readed
         * @throws IOException on error
         * @see InputStream#read()
         */
        @Override
        public int read()
            throws IOException
        {
            return this.in.read();
        }

        /**
         * @param _b byte to read
         * @return d
         * @throws IOException on error
         * @see InputStream#read(byte[])
         */
        @Override
        public int read(final byte[] _b)
            throws IOException
        {
            return this.in.read(_b);
        }

        /**
         * @param _b byte
         * @param _off offset
         * @param _len length
         * @return int
         * @throws IOException on error
         * @see InputStream#read(byte[], int, int)
         */
        @Override
        public int read(final byte[] _b,
                        final int _off,
                        final int _len)
            throws IOException
        {
            return this.in.read(_b, _off, _len);
        }

        /**
         * @throws IOException on error
         * @see InputStream#reset()
         */
        @Override
        public void reset()
            throws IOException
        {
            this.in.reset();
        }

        /**
         * @param _n n to skip
         * @return long
         * @throws IOException on error
         * @see InputStream#skip(long)
         */
        @Override
        public long skip(final long _n)
            throws IOException
        {
            return this.in.skip(_n);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy