001/*
002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     Florent Guillaume
011 */
012
013package org.nuxeo.ecm.core.blob.binary;
014
015import java.io.File;
016import java.io.FileInputStream;
017import java.io.IOException;
018import java.io.InputStream;
019import java.io.Serializable;
020
021import org.nuxeo.ecm.core.blob.BlobManager;
022import org.nuxeo.ecm.core.blob.BlobProvider;
023import org.nuxeo.runtime.api.Framework;
024
025/**
026 * A binary object that can be read, and has a length and a digest.
027 *
028 * @author Florent Guillaume
029 * @author Bogdan Stefanescu
030 */
031public class Binary implements Serializable {
032
033    private static final long serialVersionUID = 1L;
034
035    protected final String digest;
036
037    protected final String blobProviderId;
038
039    protected transient File file;
040
041    protected long length;
042
043    protected Binary(String digest, String blobProviderId) {
044        this(null, digest, blobProviderId);
045    }
046
047    public Binary(File file, String digest, String blobProviderId) {
048        this.file = file;
049        this.digest = digest;
050        this.blobProviderId = blobProviderId;
051        length = -1;
052    }
053
054    /**
055     * Compute length on demand, default implementation only works if the file referenced contains the binary original
056     * content. If you're contributing a binary type, you should adapt this in case you're encoding the content. This
057     * method is only used when users make a direct access to the binary. Persisted blobs don't use that API.
058     *
059     * @since 5.7.3
060     */
061    protected long computeLength() {
062        if (file == null) {
063            return -1;
064        }
065        return file.length();
066    }
067
068    /**
069     * Gets the length of the binary.
070     *
071     * @return the length of the binary
072     */
073    public long getLength() {
074        if (length == -1) {
075            length = computeLength();
076        }
077        return length;
078    }
079
080    /**
081     * Gets the digest algorithm from the digest length.
082     *
083     * @since 7.4
084     */
085    public String getDigestAlgorithm() {
086        // Cannot use current digest algorithm of the binary manager here since it might have changed after the binary
087        // storage
088        String digest = getDigest();
089        if (digest == null) {
090            return null;
091        }
092        return AbstractBinaryManager.DIGESTS_BY_LENGTH.get(digest.length());
093    }
094
095    /**
096     * Gets a string representation of the hex digest of the binary.
097     *
098     * @return the digest, characters are in the range {@code [0-9a-f]}
099     */
100    public String getDigest() {
101        return digest;
102    }
103
104    /**
105     * Gets the blob provider which created this blob.
106     * <p>
107     * This is usually the repository name.
108     *
109     * @return the blob provider id
110     * @since 7.3
111     */
112    public String getBlobProviderId() {
113        return blobProviderId;
114    }
115
116    /**
117     * Gets an input stream for the binary.
118     *
119     * @return the input stream
120     * @throws IOException
121     */
122    public InputStream getStream() throws IOException {
123        return new FileInputStream(file);
124    }
125
126    @Override
127    public String toString() {
128        return getClass().getSimpleName() + '(' + digest + ')';
129    }
130
131    public File getFile() {
132        return file;
133    }
134
135    private void writeObject(java.io.ObjectOutputStream oos) throws IOException, ClassNotFoundException {
136        oos.defaultWriteObject();
137    }
138
139    private void readObject(java.io.ObjectInputStream ois) throws IOException, ClassNotFoundException {
140        ois.defaultReadObject();
141        file = recomputeFile();
142    }
143
144    /**
145     * Recomputes the file attribute by getting it from a new Binary for the same digest.
146     */
147    protected File recomputeFile() {
148        BlobManager bm = Framework.getService(BlobManager.class);
149        BlobProvider bp = bm.getBlobProvider(blobProviderId);
150        return bp.getBinaryManager().getBinary(digest).file;
151    }
152
153}