View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  
22  
23  package org.opencastproject.util;
24  
25  import java.io.BufferedInputStream;
26  import java.io.File;
27  import java.io.FileInputStream;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.io.Serializable;
31  import java.security.MessageDigest;
32  import java.security.NoSuchAlgorithmException;
33  
34  import javax.xml.bind.annotation.XmlAccessType;
35  import javax.xml.bind.annotation.XmlAccessorType;
36  import javax.xml.bind.annotation.XmlAttribute;
37  import javax.xml.bind.annotation.XmlType;
38  import javax.xml.bind.annotation.XmlValue;
39  
40  /**
41   * This class stores value and type of a generated checksum.
42   */
43  @XmlAccessorType(XmlAccessType.NONE)
44  @XmlType(name = "checksum", namespace = "http://mediapackage.opencastproject.org")
45  public final class Checksum implements Serializable {
46  
47    /** Serial version uid */
48    private static final long serialVersionUID = 1L;
49  
50    /** The checksum value */
51    @XmlValue
52    protected String value = null;
53  
54    /** The checksum type */
55    @XmlAttribute(name = "type")
56    protected ChecksumType type = null;
57  
58    /** Needed by JAXB */
59    public Checksum() {
60    }
61  
62    /**
63     * Creates a new checksum object of the specified value and checksum type.
64     *
65     * @param value
66     *          the value
67     * @param type
68     *          the type
69     */
70    private Checksum(String value, ChecksumType type) {
71      if (value == null) {
72        throw new IllegalArgumentException("Checksum value is null");
73      }
74      if (type == null) {
75        throw new IllegalArgumentException("Checksum type is null");
76      }
77      this.value = value;
78      this.type = type;
79    }
80  
81    /**
82     * Returns the checksum type.
83     *
84     * @return the type
85     */
86    public ChecksumType getType() {
87      return type;
88    }
89  
90    /**
91     * Returns the checksum value.
92     *
93     * @return the value
94     */
95    public String getValue() {
96      return value;
97    }
98  
99    /**
100    * Converts the checksum to a hex string.
101    *
102    * @param data
103    *          the digest
104    * @return the digest hex representation
105    */
106   public static String convertToHex(byte[] data) {
107     final StringBuffer buf = new StringBuffer();
108     for (int i = 0; i < data.length; i++) {
109       int halfbyte = (data[i] >>> 4) & 0x0F;
110       int twoHalfs = 0;
111       do {
112         if ((0 <= halfbyte) && (halfbyte <= 9)) {
113           buf.append((char) ('0' + halfbyte));
114         } else {
115           buf.append((char) ('a' + (halfbyte - 10)));
116         }
117         halfbyte = data[i] & 0x0F;
118       } while (twoHalfs++ < 1);
119     }
120     return buf.toString();
121   }
122 
123   @Override
124   public boolean equals(Object obj) {
125     if (obj instanceof Checksum) {
126       Checksum c = (Checksum) obj;
127       return type.equals(c.type) && value.equals(c.value);
128     }
129     return false;
130   }
131 
132   @Override
133   public int hashCode() {
134     return value.hashCode();
135   }
136 
137   @Override
138   public String toString() {
139     return value + " (" + type + ")";
140   }
141 
142   /**
143    * Creates a checksum from a string in the form "value (type)".
144    *
145    * @param checksum
146    *         the checksum in string form
147    * @return the checksum
148    * @throws NoSuchAlgorithmException
149    *           if the checksum of the specified type cannot be created
150    */
151   public static Checksum fromString(String checksum) throws NoSuchAlgorithmException {
152     String[] checksumParts = checksum.split(" ");
153 
154     if (checksumParts.length != 2) {
155       throw new IllegalArgumentException("Invalid string for checksum!");
156     }
157 
158     String value = checksumParts[0];
159     String type = checksumParts[1].replace("(","").replace(")", "");
160 
161     return create(type, value);
162   }
163 
164   /**
165    * Creates a checksum of type <code>type</code> and value <code>value</code>.
166    *
167    * @param type
168    *          the checksum type name
169    * @param value
170    *          the checksum value
171    * @return the checksum
172    * @throws NoSuchAlgorithmException
173    *           if the checksum of the specified type cannot be created
174    */
175   public static Checksum create(String type, String value) throws NoSuchAlgorithmException {
176     ChecksumType t = ChecksumType.fromString(type);
177     return new Checksum(value, t);
178   }
179 
180   /**
181    * Creates a checksum of type <code>type</code> and value <code>value</code>.
182    *
183    * @param type
184    *          the checksum type
185    * @param value
186    *          the checksum value
187    * @return the checksum
188    */
189   public static Checksum create(ChecksumType type, String value) {
190     return new Checksum(value, type);
191   }
192 
193   /**
194    * Creates a checksum of type <code>type</code> from the given file.
195    *
196    * @param type
197    *          the checksum type
198    * @param file
199    *          the file
200    * @return the checksum
201    * @throws IOException
202    *           if the file cannot be accessed
203    */
204   public static Checksum create(ChecksumType type, File file) throws IOException {
205     return create(type, new BufferedInputStream(new FileInputStream(file)));
206   }
207 
208   /**
209    * Creates a checksum of type <code>type</code> from the given input stream.
210    * The stream gets closed afterwards.
211    */
212   public static Checksum create(ChecksumType type, InputStream is) throws IOException {
213     MessageDigest checksum;
214     try {
215       checksum = MessageDigest.getInstance(type.getName());
216     } catch (NoSuchAlgorithmException e) {
217       throw new IllegalStateException("This system does not support checksums of type " + type.getName());
218     }
219     try {
220       byte[] bytes = new byte[1024];
221       int len = 0;
222       while ((len = is.read(bytes)) >= 0) {
223         checksum.update(bytes, 0, len);
224       }
225     } finally {
226       IoSupport.closeQuietly(is);
227     }
228     return new Checksum(convertToHex(checksum.digest()), type);
229   }
230 
231 }