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 15 package de.softwareforge.testing.postgres.embedded; 16 17 import static com.google.common.base.Preconditions.checkState; 18 19 import jakarta.annotation.Nonnull; 20 import jakarta.annotation.Nullable; 21 import java.io.IOException; 22 import java.io.InputStream; 23 24 import com.google.common.hash.Hashing; 25 import com.google.common.hash.HashingInputStream; 26 import com.google.common.io.BaseEncoding; 27 import com.google.common.io.ByteStreams; 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 <i>should</i> implement 33 * {@link Object#toString()}. 34 * 35 * @since 3.0 36 */ 37 public interface NativeBinaryLocator { 38 39 String INSTALL_DIRECTORY_PREFIX = "bin-"; 40 41 /** 42 * Returns an input stream from which the contents of the binary archive can be read. 43 * 44 * @return An input stream. May return null. 45 * @throws IOException If the archive could not be located or read. 46 */ 47 @Nullable 48 InputStream getInputStream() throws IOException; 49 50 /** 51 * Returns a string identifier that represents the archive returned. This identifier should be stable (same value for the same archive), even across 52 * multiple JVM invocations. The value must only contain characters that can be used as a legal file name. 53 * <p> 54 * The default implementation needs to read the full archive contents and is relatively slow. Implementations of this interface can override this method to 55 * provide a faster way to create a stable identifier based on the specific implementation. 56 * <p> 57 * The default implementation hashes the archive contents and uses it to return a stable file name. 58 * 59 * @return A stable indentifier that can be used as a file name. 60 * @throws IOException If the stream could not be read. 61 */ 62 @Nonnull 63 @SuppressWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE") // false positive 64 default String getIdentifier() throws IOException { 65 try (InputStream installationArchive = getInputStream()) { 66 checkState(installationArchive != null, "Locator '%s' did not find a suitable archive to unpack!", toString()); 67 try (HashingInputStream hashStream = new HashingInputStream(Hashing.murmur3_128(), installationArchive)) { 68 ByteStreams.exhaust(hashStream); 69 return INSTALL_DIRECTORY_PREFIX + BaseEncoding.base16().encode(hashStream.hash().asBytes()); 70 } 71 } 72 } 73 }