001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.shiro.lang.util; 020 021import org.apache.shiro.lang.codec.Base64; 022import org.apache.shiro.lang.codec.CodecSupport; 023import org.apache.shiro.lang.codec.Hex; 024 025import java.io.File; 026import java.io.InputStream; 027import java.util.Arrays; 028 029/** 030 * Very simple {@link ByteSource ByteSource} implementation that maintains an internal {@code byte[]} array and uses the 031 * {@link Hex Hex} and {@link Base64 Base64} codec classes to support the 032 * {@link #toHex() toHex()} and {@link #toBase64() toBase64()} implementations. 033 * <p/> 034 * The constructors on this class accept the following implicit byte-backed data types and will convert them to 035 * a byte-array automatically: 036 * <ul> 037 * <li>byte[]</li> 038 * <li>char[]</li> 039 * <li>String</li> 040 * <li>{@link ByteSource ByteSource}</li> 041 * <li>{@link File File}</li> 042 * <li>{@link InputStream InputStream}</li> 043 * </ul> 044 * 045 * @since 1.0 046 */ 047@SuppressWarnings("checkstyle:BooleanExpressionComplexity") 048public class SimpleByteSource implements ByteSource { 049 050 private final byte[] bytes; 051 private String cachedHex; 052 private String cachedBase64; 053 054 public SimpleByteSource(byte[] bytes) { 055 this.bytes = bytes; 056 } 057 058 /** 059 * Creates an instance by converting the characters to a byte array (assumes UTF-8 encoding). 060 * 061 * @param chars the source characters to use to create the underlying byte array. 062 * @since 1.1 063 */ 064 public SimpleByteSource(char[] chars) { 065 this.bytes = CodecSupport.toBytes(chars); 066 } 067 068 /** 069 * Creates an instance by converting the String to a byte array (assumes UTF-8 encoding). 070 * 071 * @param string the source string to convert to a byte array (assumes UTF-8 encoding). 072 * @since 1.1 073 */ 074 public SimpleByteSource(String string) { 075 this.bytes = CodecSupport.toBytes(string); 076 } 077 078 /** 079 * Creates an instance using the sources bytes directly - it does not create a copy of the 080 * argument's byte array. 081 * 082 * @param source the source to use to populate the underlying byte array. 083 * @since 1.1 084 */ 085 public SimpleByteSource(ByteSource source) { 086 this.bytes = source.getBytes(); 087 } 088 089 /** 090 * Creates an instance by converting the file to a byte array. 091 * 092 * @param file the file from which to acquire bytes. 093 * @since 1.1 094 */ 095 public SimpleByteSource(File file) { 096 this.bytes = new BytesHelper().getBytes(file); 097 } 098 099 /** 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}