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 */ 037@SuppressWarnings("PMD.ImplicitFunctionalInterface") 038public interface NativeBinaryLocator { 039 040 String INSTALL_DIRECTORY_PREFIX = "bin-"; 041 042 /** 043 * Returns an input stream from which the contents of the binary archive can be read. 044 * 045 * @return An input stream. May return null. 046 * @throws IOException If the archive could not be located or read. 047 */ 048 @Nullable 049 InputStream getInputStream() throws IOException; 050 051 /** 052 * Returns a string identifier that represents the archive returned. This identifier should be stable (same value for the same archive), even across 053 * multiple JVM invocations. The value must only contain characters that can be used as a legal file name. 054 * <p> 055 * The default implementation needs to read the full archive contents and is relatively slow. Implementations of this interface can override this method to 056 * provide a faster way to create a stable identifier based on the specific implementation. 057 * <p> 058 * The default implementation hashes the archive contents and uses it to return a stable file name. 059 * 060 * @return A stable indentifier that can be used as a file name. 061 * @throws IOException If the stream could not be read. 062 */ 063 @Nonnull 064 @SuppressWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE") // false positive 065 default String getIdentifier() throws IOException { 066 try (InputStream installationArchive = getInputStream()) { 067 checkState(installationArchive != null, "Locator '%s' did not find a suitable archive to unpack!", toString()); 068 try (HashingInputStream hashStream = new HashingInputStream(Hashing.murmur3_128(), installationArchive)) { 069 ByteStreams.exhaust(hashStream); 070 return INSTALL_DIRECTORY_PREFIX + BaseEncoding.base16().encode(hashStream.hash().asBytes()); 071 } 072 } 073 } 074}