1 /*
2 * #%L
3 * Nuiton Utils
4 * %%
5 * Copyright (C) 2004 - 2010 CodeLutin
6 * %%
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Lesser Public License for more details.
16 *
17 * You should have received a copy of the GNU General Lesser Public
18 * License along with this program. If not, see
19 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
20 * #L%
21 */
22
23 package org.nuiton.util;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27
28 import java.io.BufferedInputStream;
29 import java.io.ByteArrayInputStream;
30 import java.io.File;
31 import java.io.FileInputStream;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.security.DigestInputStream;
35 import java.security.MessageDigest;
36 import java.security.NoSuchAlgorithmException;
37
38 import static org.nuiton.i18n.I18n.t;
39
40 /**
41 * MD5InputStream, a subclass of FilterInputStream implementing MD5
42 * functionality on a stream.
43 *
44 * @author Tony Chemit - chemit@codelutin.com
45 */
46 public class MD5InputStream extends DigestInputStream {
47
48 /** Class logger. */
49 private static final Log log = LogFactory.getLog(MD5InputStream.class);
50
51 protected static MessageDigest getMD5Digest() throws IllegalStateException {
52 try {
53 MessageDigest digest = MessageDigest.getInstance("MD5");
54 return digest;
55 } catch (NoSuchAlgorithmException e) {
56 if (log.isErrorEnabled()) {
57 log.error(t("nuitonutil.error.could.not.find.MD5"), e);
58 }
59 throw new IllegalStateException(e);
60 }
61 }
62
63 /**
64 * Compute the MD5 for the given {@code input}.
65 *
66 * <b>Note:</b> The the stream will be closed after calling the method
67 * even if something was wrong.
68 *
69 * @param input the stream to parse
70 * @return the MD5 hash for the given input stream
71 * @throws IOException if any pb while reading in stream or digest
72 */
73 public static byte[] hash(InputStream input) throws IOException {
74 MD5InputStream in = new MD5InputStream(input);
75 try {
76 while (in.read() != -1) {
77 // read a caracter on stream
78 }
79
80 byte[] result = in.hash();
81 return result;
82 } finally {
83 in.close();
84 }
85 }
86
87
88 /**
89 * Compute the MD5 for the given {@code input} file.
90 *
91 * @param input the File stream to parse
92 * @return the MD5 hash for the given input File
93 * @throws IOException if any pb while reading in stream or digest
94 */
95 public static byte[] hash(File input) throws IOException {
96 InputStream inputStream = new BufferedInputStream(new FileInputStream(input));
97 byte[] result;
98 try {
99 result = hash(inputStream);
100 } finally {
101 inputStream.close();
102 }
103 return result;
104 }
105
106
107 /**
108 * Compute the MD5 for the given {@code input} sring.
109 *
110 * @param input the stream to parse
111 * @return the MD5 hash for the given input String
112 * @throws IOException if any pb while reading in stream or digest
113 */
114 public static byte[] hash(String input) throws IOException {
115 byte[] result = hash(new ByteArrayInputStream(input.getBytes()));
116 return result;
117 }
118
119 /** length of readed stream */
120 protected long streamLength;
121
122 /**
123 * Creates a MD5InputStream
124 *
125 * @param in The input stream
126 */
127 public MD5InputStream(InputStream in) {
128 super(in, getMD5Digest());
129 }
130
131 @Override
132 public int read() throws IOException {
133 int c = super.read();
134
135 if (c == -1) {
136 return -1;
137 }
138 streamLength++;
139 return c;
140 }
141
142 @Override
143 public int read(byte bytes[], int offset, int length) throws IOException {
144 int r;
145
146 if ((r = super.read(bytes, offset, length)) == -1) {
147 return r;
148 }
149 streamLength += r;
150 return r;
151 }
152
153 /**
154 * Returns array of bytes representing hash of the stream as finalized for
155 * the current state.
156 *
157 * @return hash
158 * @see MessageDigest#digest()
159 */
160 public byte[] hash() {
161 return getMessageDigest().digest();
162 }
163
164 public long getStreamLength() {
165 return streamLength;
166 }
167 }
168