001/* 002 * Licensed under the Apache License, Version 2.0 (the "License"); 003 * you may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at 005 * 006 * http://www.apache.org/licenses/LICENSE-2.0 007 * 008 * Unless required by applicable law or agreed to in writing, software 009 * distributed under the License is distributed on an "AS IS" BASIS, 010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 011 * See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014 015package de.softwareforge.testing.postgres.embedded; 016 017import static com.google.common.base.Preconditions.checkState; 018 019import jakarta.annotation.Nonnull; 020import jakarta.annotation.Nullable; 021import java.io.IOException; 022import java.io.InputStream; 023 024import com.google.common.hash.Hashing; 025import com.google.common.hash.HashingInputStream; 026import com.google.common.io.BaseEncoding; 027import com.google.common.io.ByteStreams; 028 029/** 030 * Locates a native binary on the Filesystem. If necessary, it should download the binary first from the network. 031 * <p> 032 * Implementations of this class <b>must</b> implement {@link Object#hashCode()} and {@link Object#equals(Object)} and <i>should</i> implement 033 * {@link Object#toString()}. 034 * 035 * @since 3.0 036 */ 037public interface NativeBinaryLocator { 038 039 String INSTALL_DIRECTORY_PREFIX = "bin-"; 040 041 /** 042 * Returns an input stream from which the contents of the binary archive can be read. 043 * 044 * @return An input stream. May return null. 045 * @throws IOException If the archive could not be located or read. 046 */ 047 @Nullable 048 InputStream getInputStream() throws IOException; 049 050 /** 051 * Returns a string identifier that represents the archive returned. This identifier should be stable (same value for the same archive), even across 052 * multiple JVM invocations. The value must only contain characters that can be used as a legal file name. 053 * <p> 054 * The default implementation needs to read the full archive contents and is relatively slow. Implementations of this interface can override this method to 055 * provide a faster way to create a stable identifier based on the specific implementation. 056 * <p> 057 * The default implementation hashes the archive contents and uses it to return a stable file name. 058 * 059 * @return A stable indentifier that can be used as a file name. 060 * @throws IOException If the stream could not be read. 061 */ 062 @Nonnull 063 @SuppressWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE") // false positive 064 default String getIdentifier() throws IOException { 065 try (InputStream installationArchive = getInputStream()) { 066 checkState(installationArchive != null, "Locator '%s' did not find a suitable archive to unpack!", toString()); 067 try (HashingInputStream hashStream = new HashingInputStream(Hashing.murmur3_128(), installationArchive)) { 068 ByteStreams.exhaust(hashStream); 069 return INSTALL_DIRECTORY_PREFIX + BaseEncoding.base16().encode(hashStream.hash().asBytes()); 070 } 071 } 072 } 073}