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.Base64;
032    import org.jboss.dna.common.util.CheckArg;
033    import org.jboss.dna.common.util.Logger;
034    import org.jboss.dna.graph.GraphI18n;
035    import org.jboss.dna.graph.properties.Binary;
036    import org.jboss.dna.graph.properties.ValueComparators;
037    
038    /**
039     * An implementation of {@link Binary} that keeps the binary data in-memory.
040     * 
041     * @author Randall Hauch
042     */
043    @Immutable
044    public class InMemoryBinary implements Binary {
045    
046        protected static final Set<String> ALGORITHMS_NOT_FOUND_AND_LOGGED = new CopyOnWriteArraySet<String>();
047        private static final String SHA1DIGEST_NAME = "SHA-1";
048        private static final byte[] NO_HASH = new byte[] {};
049    
050        /**
051         */
052        private static final long serialVersionUID = 8792863149767123559L;
053    
054        protected static final byte[] EMPTY_CONTENT = new byte[0];
055    
056        private final byte[] bytes;
057        private byte[] sha1hash;
058    
059        public InMemoryBinary( byte[] bytes ) {
060            CheckArg.isNotNull(bytes, "bytes");
061            this.bytes = bytes;
062        }
063    
064        /**
065         * {@inheritDoc}
066         */
067        public long getSize() {
068            return this.bytes.length;
069        }
070    
071        /**
072         * {@inheritDoc}
073         * 
074         * @see org.jboss.dna.graph.properties.Binary#getHash()
075         */
076        public byte[] getHash() {
077            if (sha1hash == null) {
078                // Omnipotent, so doesn't matter if we recompute in concurrent threads ...
079                try {
080                    sha1hash = getHash(SHA1DIGEST_NAME);
081                } catch (NoSuchAlgorithmException e) {
082                    if (ALGORITHMS_NOT_FOUND_AND_LOGGED.add(SHA1DIGEST_NAME)) {
083                        Logger.getLogger(getClass()).error(e, GraphI18n.messageDigestNotFound, SHA1DIGEST_NAME);
084                    }
085                    sha1hash = NO_HASH;
086                }
087            }
088            return sha1hash;
089        }
090    
091        /**
092         * Get the hash of the contents, using the digest identified by the supplied name.
093         * 
094         * @param digestName the name of the hashing function (or {@link MessageDigest message digest}) that should be used
095         * @return the hash of the contents as a byte array
096         * @throws NoSuchAlgorithmException if the supplied algorithm could not be found
097         */
098        protected byte[] getHash( String digestName ) throws NoSuchAlgorithmException {
099            MessageDigest digest = MessageDigest.getInstance(digestName);
100            assert digest != null;
101            return digest.digest(bytes);
102        }
103    
104        /**
105         * {@inheritDoc}
106         */
107        public byte[] getBytes() {
108            return this.bytes;
109        }
110    
111        /**
112         * {@inheritDoc}
113         */
114        public InputStream getStream() {
115            return new ByteArrayInputStream(this.bytes);
116        }
117    
118        /**
119         * {@inheritDoc}
120         */
121        public void acquire() {
122            // do nothing
123        }
124    
125        /**
126         * {@inheritDoc}
127         */
128        public void release() {
129            // do nothing
130        }
131    
132        /**
133         * {@inheritDoc}
134         */
135        public int compareTo( Binary o ) {
136            return ValueComparators.BINARY_COMPARATOR.compare(this, o);
137        }
138    
139        /**
140         * {@inheritDoc}
141         */
142        @Override
143        public boolean equals( Object obj ) {
144            if (obj == this) return true;
145            if (obj instanceof Binary) {
146                Binary that = (Binary)obj;
147                if (this.getSize() != that.getSize()) return false;
148                return ValueComparators.BINARY_COMPARATOR.compare(this, that) == 0;
149            }
150            return false;
151        }
152    
153        /**
154         * {@inheritDoc}
155         */
156        @Override
157        public String toString() {
158            StringBuilder sb = new StringBuilder(super.toString());
159            sb.append(" len=").append(getSize()).append("; [");
160            sb.append(Base64.encodeBytes(this.bytes));
161            return sb.toString();
162        }
163    
164    }