Package org.restlet.representation

Source Code of org.restlet.representation.DigesterRepresentation

/**
* Copyright 2005-2011 Noelios Technologies.
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: LGPL 3.0 or LGPL 2.1 or CDDL 1.0 or EPL 1.0 (the
* "Licenses"). You can select the license that you prefer but you may not use
* this file except in compliance with one of these Licenses.
*
* You can obtain a copy of the LGPL 3.0 license at
* http://www.opensource.org/licenses/lgpl-3.0.html
*
* You can obtain a copy of the LGPL 2.1 license at
* http://www.opensource.org/licenses/lgpl-2.1.php
*
* You can obtain a copy of the CDDL 1.0 license at
* http://www.opensource.org/licenses/cddl1.php
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://www.noelios.com/products/restlet-engine
*
* Restlet is a registered trademark of Noelios Technologies.
*/

package org.restlet.representation;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;

import org.restlet.Context;
import org.restlet.data.Digest;
import org.restlet.engine.io.BioUtils;
import org.restlet.engine.io.NioUtils;
import org.restlet.util.WrapperRepresentation;

/**
* Representation capable of computing a digest. It wraps another representation
* and allows to get the computed digest of the wrapped entity after reading or
* writing operations. The final digest value is guaranteed to be correct only
* after the wrapped representation has been entirely exhausted (that is to say
* read or written).<br>
* <br>
* This wrapper allows to get the computed digest at the same time the
* representation is read or written. It does not need two separate operations
* which may require specific attention for transient representations.
*
* @see Representation#isTransient().
*
* @author Jerome Louvel
*/
public class DigesterRepresentation extends WrapperRepresentation {
    /** The digest algorithm. */
    private final String algorithm;

    /** The computed digest value. */
    private volatile MessageDigest computedDigest;

    /**
     * Constructor.<br>
     * By default, the instance relies on the {@link Digest#ALGORITHM_MD5}
     * digest algorithm.
     *
     * @param wrappedRepresentation
     *            The wrapped representation.
     * @throws NoSuchAlgorithmException
     */
    public DigesterRepresentation(Representation wrappedRepresentation)
            throws NoSuchAlgorithmException {
        this(wrappedRepresentation, Digest.ALGORITHM_MD5);
    }

    /**
     * Constructor.<br>
     *
     * @param wrappedRepresentation
     *            The wrapped representation.
     * @param algorithm
     *            The digest algorithm.
     * @throws NoSuchAlgorithmException
     */
    public DigesterRepresentation(Representation wrappedRepresentation,
            String algorithm) throws NoSuchAlgorithmException {
        super(wrappedRepresentation);
        this.algorithm = algorithm;
        this.computedDigest = MessageDigest.getInstance(algorithm);
    }

    /**
     * Check that the digest computed from the representation content and the
     * digest declared by the representation are the same.<br>
     * Since this method relies on the {@link #computeDigest(String)} method,
     * and since this method reads entirely the representation's stream, user
     * must take care of the content of the representation in case the latter is
     * transient.
     *
     * {@link #isTransient}
     *
     * @return True if both digests are not null and equals.
     */
    public boolean checkDigest() {
        Digest digest = getDigest();
        return (digest != null && digest.equals(getComputedDigest()));
    }

    /**
     * Check that the digest computed from the representation content and the
     * digest declared by the representation are the same. It also first checks
     * that the algorithms are the same.<br>
     * Since this method relies on the {@link #computeDigest(String)} method,
     * and since this method reads entirely the representation's stream, user
     * must take care of the content of the representation in case the latter is
     * transient.
     *
     * {@link #isTransient}
     *
     * @param algorithm
     *            The algorithm used to compute the digest to compare with. See
     *            constant values in {@link org.restlet.data.Digest}.
     * @return True if both digests are not null and equals.
     */
    public boolean checkDigest(String algorithm) {
        boolean result = false;

        if (this.algorithm != null && this.algorithm.equals(algorithm)) {
            result = checkDigest();
        } else {
            Digest digest = getDigest();

            if (digest != null) {
                if (algorithm.equals(digest.getAlgorithm())) {
                    result = digest.equals(computeDigest(algorithm));
                }
            }
        }

        return result;
    }

    /**
     * Compute the representation digest according to MD5 algorithm.<br>
     * If case this algorithm is the same than the one provided at
     * instantiation, the computation operation is made with the current stored
     * computed value and does not require to exhaust entirely the
     * representation's stream.
     */
    public Digest computeDigest() {
        return computeDigest(Digest.ALGORITHM_MD5);
    }

    /**
     * Compute the representation digest according to the given algorithm.<br>
     * Since this method reads entirely the representation's stream, user must
     * take care of the content of the representation in case the latter is
     * transient.
     *
     * {@link #isTransient}
     *
     * @param algorithm
     *            The algorithm used to compute the digest. See constant values
     *            in {@link org.restlet.data.Digest}.
     * @return The computed digest or null if the digest cannot be computed.
     */
    public Digest computeDigest(String algorithm) {
        Digest result = null;

        if (this.algorithm != null && this.algorithm.equals(algorithm)) {
            result = getComputedDigest();
        } else if (isAvailable()) {
            try {
                java.security.MessageDigest md = java.security.MessageDigest
                        .getInstance(algorithm);
                java.security.DigestInputStream dis = new java.security.DigestInputStream(
                        getStream(), md);
                org.restlet.engine.io.BioUtils.exhaust(dis);
                result = new org.restlet.data.Digest(algorithm, md.digest());
            } catch (java.security.NoSuchAlgorithmException e) {
                Context.getCurrentLogger().log(Level.WARNING,
                        "Unable to check the digest of the representation.", e);
            } catch (IOException e) {
                Context.getCurrentLogger().log(Level.WARNING,
                        "Unable to check the digest of the representation.", e);
            }
        }

        return result;
    }

    /**
     * Exhausts the content of the representation by reading it and silently
     * discarding anything read.
     *
     * @return The number of bytes consumed or -1 if unknown.
     */
    public long exhaust() throws IOException {
        long result = -1L;

        if (isAvailable()) {
            result = BioUtils.exhaust(getStream());
        }

        return result;
    }

    @Override
    public ReadableByteChannel getChannel() throws IOException {
        return NioUtils.getChannel(getStream());
    }

    /**
     * Returns the current computed digest value of the representation. User
     * must be aware that, if the representation has not been entirely read or
     * written, the computed digest value may not be accurate.
     *
     * @return The current computed digest value.
     */
    public Digest getComputedDigest() {
        return new Digest(this.algorithm, computedDigest.digest());
    }

    @Override
    public Reader getReader() throws IOException {
        return BioUtils.getReader(getStream(), getCharacterSet());
    }

    /**
     * {@inheritDoc}<br>
     *
     * The stream of the underlying representation is wrapped with a new
     * instance of the {@link DigestInputStream} class, which allows to compute
     * progressively the digest value.
     */
    @Override
    public InputStream getStream() throws IOException {
        return new DigestInputStream(getWrappedRepresentation().getStream(),
                this.computedDigest);
    }

    @Override
    public String getText() throws IOException {
        String result = null;

        if (isAvailable()) {
            if (getSize() == 0) {
                result = "";
            } else {
                java.io.StringWriter sw = new java.io.StringWriter();
                write(sw);
                result = sw.toString();
            }
        }

        return result;
    }

    /**
     * {@inheritDoc}<br>
     *
     * The output stream is wrapped with a new instance of the
     * {@link DigestOutputStream} class, which allows to compute progressively
     * the digest value.
     */
    @Override
    public void write(OutputStream outputStream) throws IOException {
        getWrappedRepresentation().write(
                new DigestOutputStream(outputStream, this.computedDigest));
    }

    @Override
    public void write(WritableByteChannel writableChannel) throws IOException {
        write(NioUtils.getOutputStream(writableChannel));
    }

    @Override
    public void write(Writer writer) throws IOException {
        write(BioUtils.getOutputStream(writer));
    }
}
TOP

Related Classes of org.restlet.representation.DigesterRepresentation

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.