View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.shiro.lang.util;
20  
21  import org.apache.shiro.lang.codec.Base64;
22  import org.apache.shiro.lang.codec.CodecSupport;
23  import org.apache.shiro.lang.codec.Hex;
24  
25  import java.io.File;
26  import java.io.InputStream;
27  import java.util.Arrays;
28  
29  /**
30   * Very simple {@link ByteSource ByteSource} implementation that maintains an internal {@code byte[]} array and uses the
31   * {@link Hex Hex} and {@link Base64 Base64} codec classes to support the
32   * {@link #toHex() toHex()} and {@link #toBase64() toBase64()} implementations.
33   * <p/>
34   * The constructors on this class accept the following implicit byte-backed data types and will convert them to
35   * a byte-array automatically:
36   * <ul>
37   * <li>byte[]</li>
38   * <li>char[]</li>
39   * <li>String</li>
40   * <li>{@link ByteSource ByteSource}</li>
41   * <li>{@link File File}</li>
42   * <li>{@link InputStream InputStream}</li>
43   * </ul>
44   *
45   * @since 1.0
46   */
47  @SuppressWarnings("checkstyle:BooleanExpressionComplexity")
48  public class SimpleByteSource implements ByteSource {
49  
50      private final byte[] bytes;
51      private String cachedHex;
52      private String cachedBase64;
53  
54      public SimpleByteSource(byte[] bytes) {
55          this.bytes = bytes;
56      }
57  
58      /**
59       * Creates an instance by converting the characters to a byte array (assumes UTF-8 encoding).
60       *
61       * @param chars the source characters to use to create the underlying byte array.
62       * @since 1.1
63       */
64      public SimpleByteSource(char[] chars) {
65          this.bytes = CodecSupport.toBytes(chars);
66      }
67  
68      /**
69       * Creates an instance by converting the String to a byte array (assumes UTF-8 encoding).
70       *
71       * @param string the source string to convert to a byte array (assumes UTF-8 encoding).
72       * @since 1.1
73       */
74      public SimpleByteSource(String string) {
75          this.bytes = CodecSupport.toBytes(string);
76      }
77  
78      /**
79       * Creates an instance using the sources bytes directly - it does not create a copy of the
80       * argument's byte array.
81       *
82       * @param source the source to use to populate the underlying byte array.
83       * @since 1.1
84       */
85      public SimpleByteSource(ByteSource source) {
86          this.bytes = source.getBytes();
87      }
88  
89      /**
90       * Creates an instance by converting the file to a byte array.
91       *
92       * @param file the file from which to acquire bytes.
93       * @since 1.1
94       */
95      public SimpleByteSource(File file) {
96          this.bytes = new BytesHelper().getBytes(file);
97      }
98  
99      /**
100      * Creates an instance by converting the stream to a byte array.
101      *
102      * @param stream the stream from which to acquire bytes.
103      * @since 1.1
104      */
105     public SimpleByteSource(InputStream stream) {
106         this.bytes = new BytesHelper().getBytes(stream);
107     }
108 
109     /**
110      * Returns {@code true} if the specified object is a recognized data type that can be easily converted to
111      * bytes by instances of this class, {@code false} otherwise.
112      * <p/>
113      * This implementation returns {@code true} IFF the specified object is an instance of one of the following
114      * types:
115      * <ul>
116      * <li>{@code byte[]}</li>
117      * <li>{@code char[]}</li>
118      * <li>{@link ByteSource}</li>
119      * <li>{@link String}</li>
120      * <li>{@link File}</li>
121      * </li>{@link InputStream}</li>
122      * </ul>
123      *
124      * @param o the object to test to see if it can be easily converted to bytes by instances of this class.
125      * @return {@code true} if the specified object can be easily converted to bytes by instances of this class,
126      * {@code false} otherwise.
127      * @since 1.2
128      */
129     public static boolean isCompatible(Object o) {
130         return o instanceof byte[] || o instanceof char[] || o instanceof String
131                 || o instanceof ByteSource || o instanceof File || o instanceof InputStream;
132     }
133 
134     public static ByteSource empty() {
135         return new SimpleByteSource(new byte[] {});
136     }
137 
138     @Override
139     public byte[] getBytes() {
140         return Arrays.copyOf(this.bytes, this.bytes.length);
141     }
142 
143     @Override
144     public boolean isEmpty() {
145         return this.bytes == null || this.bytes.length == 0;
146     }
147 
148     @Override
149     public String toHex() {
150         if (this.cachedHex == null) {
151             this.cachedHex = Hex.encodeToString(getBytes());
152         }
153         return this.cachedHex;
154     }
155 
156     @Override
157     public String toBase64() {
158         if (this.cachedBase64 == null) {
159             this.cachedBase64 = Base64.encodeToString(getBytes());
160         }
161         return this.cachedBase64;
162     }
163 
164     @Override
165     public String toString() {
166         return toBase64();
167     }
168 
169     @Override
170     public int hashCode() {
171         if (this.bytes == null || this.bytes.length == 0) {
172             return 0;
173         }
174         return Arrays.hashCode(this.bytes);
175     }
176 
177     @Override
178     public boolean equals(Object o) {
179         if (o == this) {
180             return true;
181         }
182         if (o instanceof ByteSource) {
183             ByteSource bs = (ByteSource) o;
184             return Arrays.equals(getBytes(), bs.getBytes());
185         }
186         return false;
187     }
188 
189     //will probably be removed in Shiro 2.0.  See SHIRO-203:
190     //https://issues.apache.org/jira/browse/SHIRO-203
191     private static final class BytesHelper extends CodecSupport {
192         public byte[] getBytes(File file) {
193             return toBytes(file);
194         }
195 
196         public byte[] getBytes(InputStream stream) {
197             return toBytes(stream);
198         }
199     }
200 }