001    /*
002     * JBoss, Home of Professional Open Source.
003     * Copyright 2008, Red Hat Middleware LLC, and individual contributors
004     * as indicated by the @author tags. See the copyright.txt file in the
005     * distribution for a full listing of individual contributors. 
006     *
007     * This is free software; you can redistribute it and/or modify it
008     * under the terms of the GNU Lesser General Public License as
009     * published by the Free Software Foundation; either version 2.1 of
010     * the License, or (at your option) any later version.
011     *
012     * This software is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015     * Lesser General Public License for more details.
016     *
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this software; if not, write to the Free
019     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021     */
022    package org.jboss.dna.graph.properties.basic;
023    
024    import java.io.ByteArrayInputStream;
025    import java.io.InputStream;
026    import java.security.MessageDigest;
027    import java.security.NoSuchAlgorithmException;
028    import java.util.Set;
029    import java.util.concurrent.CopyOnWriteArraySet;
030    import net.jcip.annotations.Immutable;
031    import org.jboss.dna.common.util.CheckArg;
032    import org.jboss.dna.common.util.Logger;
033    import org.jboss.dna.graph.GraphI18n;
034    import org.jboss.dna.graph.properties.Binary;
035    import org.jboss.dna.graph.properties.ValueComparators;
036    
037    /**
038     * An implementation of {@link Binary} that keeps the binary data in-memory.
039     * 
040     * @author Randall Hauch
041     */
042    @Immutable
043    public class InMemoryBinary implements Binary {
044    
045        protected static final Set<String> ALGORITHMS_NOT_FOUND_AND_LOGGED = new CopyOnWriteArraySet<String>();
046        private static final String SHA1DIGEST_NAME = "SHA-1";
047        private static final byte[] NO_HASH = new byte[] {};
048    
049        /**
050         */
051        private static final long serialVersionUID = 8792863149767123559L;
052    
053        protected static final byte[] EMPTY_CONTENT = new byte[0];
054    
055        private final byte[] bytes;
056        private byte[] sha1hash;
057    
058        public InMemoryBinary( byte[] bytes ) {
059            CheckArg.isNotNull(bytes, "bytes");
060            this.bytes = bytes;
061        }
062    
063        /**
064         * {@inheritDoc}
065         */
066        public long getSize() {
067            return this.bytes.length;
068        }
069    
070        /**
071         * {@inheritDoc}
072         * 
073         * @see org.jboss.dna.graph.properties.Binary#getHash()
074         */
075        public byte[] getHash() {
076            if (sha1hash == null) {
077                // Omnipotent, so doesn't matter if we recompute in concurrent threads ...
078                try {
079                    sha1hash = getHash(SHA1DIGEST_NAME);
080                } catch (NoSuchAlgorithmException e) {
081                    if (ALGORITHMS_NOT_FOUND_AND_LOGGED.add(SHA1DIGEST_NAME)) {
082                        Logger.getLogger(getClass()).error(e, GraphI18n.messageDigestNotFound, SHA1DIGEST_NAME);
083                    }
084                    sha1hash = NO_HASH;
085                }
086            }
087            return sha1hash;
088        }
089    
090        /**
091         * Get the hash of the contents, using the digest identified by the supplied name.
092         * 
093         * @param digestName the name of the hashing function (or {@link MessageDigest message digest}) that should be used
094         * @return the hash of the contents as a byte array
095         * @throws NoSuchAlgorithmException if the supplied algorithm could not be found
096         */
097        protected byte[] getHash( String digestName ) throws NoSuchAlgorithmException {
098            MessageDigest digest = MessageDigest.getInstance(digestName);
099            assert digest != null;
100            return digest.digest(bytes);
101        }
102    
103        /**
104         * {@inheritDoc}
105         */
106        public byte[] getBytes() {
107            return this.bytes;
108        }
109    
110        /**
111         * {@inheritDoc}
112         */
113        public InputStream getStream() {
114            return new ByteArrayInputStream(this.bytes);
115        }
116    
117        /**
118         * {@inheritDoc}
119         */
120        public void acquire() {
121            // do nothing
122        }
123    
124        /**
125         * {@inheritDoc}
126         */
127        public void release() {
128            // do nothing
129        }
130    
131        /**
132         * {@inheritDoc}
133         */
134        public int compareTo( Binary o ) {
135            return ValueComparators.BINARY_COMPARATOR.compare(this, o);
136        }
137    
138        /**
139         * {@inheritDoc}
140         */
141        @Override
142        public boolean equals( Object obj ) {
143            if (obj == this) return true;
144            if (obj instanceof Binary) {
145                Binary that = (Binary)obj;
146                if (this.getSize() != that.getSize()) return false;
147                return ValueComparators.BINARY_COMPARATOR.compare(this, that) == 0;
148            }
149            return false;
150        }
151    
152        /**
153         * {@inheritDoc}
154         */
155        @Override
156        public String toString() {
157            StringBuilder sb = new StringBuilder(super.toString());
158            sb.append(" len=").append(getSize()).append("; [");
159            int len = (int)Math.min(getSize(), 20l);
160            for (int i = 0; i != len; ++i) {
161                if (i != 0) sb.append(',');
162                sb.append(this.bytes[i]);
163            }
164            return sb.toString();
165        }
166    
167    }