1 /* 2 * Licensed under the Apache License, Version 2.0 (the "License"); 3 * you may not use this file except in compliance with the License. 4 * You may obtain a copy of the License at 5 * 6 * http://www.apache.org/licenses/LICENSE-2.0 7 * 8 * Unless required by applicable law or agreed to in writing, software 9 * distributed under the License is distributed on an "AS IS" BASIS, 10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 * See the License for the specific language governing permissions and 12 * limitations under the License. 13 */ 14 package de.softwareforge.testing.postgres.embedded; 15 16 import static com.google.common.base.Preconditions.checkState; 17 18 import java.io.IOException; 19 import java.io.InputStream; 20 21 import com.google.common.hash.Hashing; 22 import com.google.common.hash.HashingInputStream; 23 import com.google.common.io.BaseEncoding; 24 import com.google.common.io.ByteStreams; 25 import edu.umd.cs.findbugs.annotations.CheckForNull; 26 import edu.umd.cs.findbugs.annotations.NonNull; 27 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 28 29 /** 30 * Locates a native binary on the Filesystem. If necessary, it should download the binary first from the network. 31 * <p> 32 * Implementations of this class <b>must</b> implement {@link Object#hashCode()} and {@link Object#equals(Object)} and *should* implement {@link Object#toString()}. 33 */ 34 public interface NativeBinaryLocator { 35 36 String INSTALL_DIRECTORY_PREFIX = "bin-"; 37 38 /** 39 * Returns an input stream from which the contents of the binary archive can be read. 40 * 41 * @return An input stream. May return null. 42 * @throws IOException If the archive could not be located or read. 43 */ 44 @CheckForNull 45 InputStream getInputStream() throws IOException; 46 47 /** 48 * Returns a string identifier that represents the archive returned. This identifier should be stable (same value for the same archive), even across 49 * multiple JVM invocations. The value must only contain characters that can be used as a legal file name. 50 * <p> 51 * The default implementation needs to read the full archive contents and is relatively slow. Implementations of this interface can override this method to 52 * provide a faster way to create a stable identifier based on the specific implementation. 53 * <p> 54 * The default implementation hashes the archive contents and uses it to return a stable file name. 55 * 56 * @return A stable indentifier that can be used as a file name. 57 * @throws IOException If the stream could not be read. 58 */ 59 @NonNull 60 @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE") // false positive 61 default String getIdentifier() throws IOException { 62 try (InputStream installationArchive = getInputStream()) { 63 checkState(installationArchive != null, "Locator '%s' did not find a suitable archive to unpack!", toString()); 64 try (HashingInputStream hashStream = new HashingInputStream(Hashing.murmur3_128(), installationArchive)) { 65 ByteStreams.exhaust(hashStream); 66 return INSTALL_DIRECTORY_PREFIX + BaseEncoding.base16().encode(hashStream.hash().asBytes()); 67 } 68 } 69 } 70 }