1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package de.softwareforge.testing.postgres.embedded;
16
17 import static com.google.common.base.Preconditions.checkNotNull;
18 import static com.google.common.base.Preconditions.checkState;
19 import static java.lang.String.format;
20
21 import de.softwareforge.testing.maven.MavenArtifactLoader;
22
23 import java.io.File;
24 import java.io.FilterInputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.UncheckedIOException;
28 import java.nio.charset.StandardCharsets;
29 import java.util.Objects;
30 import java.util.function.Supplier;
31 import java.util.jar.JarEntry;
32 import java.util.jar.JarFile;
33
34 import com.google.common.base.Suppliers;
35 import com.google.common.hash.HashCode;
36 import com.google.common.hash.Hashing;
37 import com.google.common.io.BaseEncoding;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41
42
43
44
45
46
47 public final class ZonkyIOPostgresLocator implements NativeBinaryLocator {
48
49 private static final String ZONKY_GROUP_ID = "io.zonky.test.postgres";
50 private static final String ZONKY_ARTIFACT_ID_TEMPLATE = "embedded-postgres-binaries-%s-%s";
51
52 public static final Logger LOG = LoggerFactory.getLogger(ZonkyIOPostgresLocator.class);
53
54 private static final boolean PREFER_NATIVE = Boolean.getBoolean("pg-embedded.prefer-native");
55
56 private final String architecture;
57 private final String os;
58 private final String serverVersion;
59
60 private final MavenArtifactLoader artifactLoader = new MavenArtifactLoader();
61
62 private final Supplier<File> fileSupplier = Suppliers.memoize(this::loadArtifact);
63
64 ZonkyIOPostgresLocator(String serverVersion) {
65 this.serverVersion = checkNotNull(serverVersion, "serverVersion is null");
66
67 this.os = computeOS();
68 this.architecture = computeTarXzArchitectureName();
69 LOG.debug(format("Detected a %s %s system, using PostgreSQL version %s", architecture, os, serverVersion));
70 }
71
72 @Override
73 public InputStream getInputStream() throws IOException {
74 try {
75 File artifactFile = fileSupplier.get();
76 return createJarStream(artifactFile);
77 } catch (UncheckedIOException e) {
78 throw e.getCause();
79 }
80 }
81
82 @Override
83 public String getIdentifier() throws IOException {
84
85 try {
86 File artifactFile = fileSupplier.get();
87 HashCode hashCode = Hashing.murmur3_128().hashString(artifactFile.getAbsolutePath(), StandardCharsets.UTF_8);
88 return INSTALL_DIRECTORY_PREFIX + BaseEncoding.base16().encode(hashCode.asBytes());
89 } catch (UncheckedIOException e) {
90 throw e.getCause();
91 }
92 }
93
94 private File loadArtifact() {
95 try {
96 String artifactId = format(ZONKY_ARTIFACT_ID_TEMPLATE, this.os, computeJarArchitectureName());
97
98
99 if (EmbeddedUtil.IS_ALPINE_LINUX) {
100 artifactId += "-alpine";
101 }
102
103 String version = artifactLoader.findLatestVersion(ZONKY_GROUP_ID, artifactId, serverVersion);
104 File file = artifactLoader.getArtifactFile(ZONKY_GROUP_ID, artifactId, version);
105 checkState(file != null && file.exists(), "Could not locate artifact file for %s:%s", artifactId, version);
106 LOG.info(format("Using PostgreSQL version %s", version));
107 return file;
108 } catch (IOException e) {
109 throw new UncheckedIOException(e);
110 }
111 }
112
113 private InputStream createJarStream(File file) {
114 try {
115 JarFile jar = new JarFile(file);
116 String entryName = format("postgres-%s-%s", computeOS(), computeTarXzArchitectureName());
117
118
119 if (EmbeddedUtil.IS_ALPINE_LINUX) {
120 entryName += "-alpine_linux";
121 }
122
123 JarEntry jarEntry = jar.getJarEntry(entryName + ".txz");
124 checkState(jarEntry != null, "Could not locate %s in the jar file (%s)", entryName, file.getAbsoluteFile());
125
126
127 return new FilterInputStream(jar.getInputStream(jarEntry)) {
128 @Override
129 public void close() throws IOException {
130 try {
131 super.close();
132 } finally {
133 jar.close();
134 }
135 }
136 };
137 } catch (IOException e) {
138 throw new UncheckedIOException(e);
139 }
140 }
141
142 @Override
143 public String toString() {
144 return format("ZonkyIO Stream locator for PostgreSQL (arch: %s os: %s, version: %s)", architecture, os, serverVersion);
145 }
146
147 @Override
148 public boolean equals(Object o) {
149 if (this == o) {
150 return true;
151 }
152 if (o == null || getClass() != o.getClass()) {
153 return false;
154 }
155 ZonkyIOPostgresLocator/de/softwareforge/testing/postgres/embedded/ZonkyIOPostgresLocator.html#ZonkyIOPostgresLocator">ZonkyIOPostgresLocator that = (ZonkyIOPostgresLocator) o;
156 return architecture.equals(that.architecture) && os.equals(that.os) && serverVersion.equals(that.serverVersion);
157 }
158
159 @Override
160 public int hashCode() {
161 return Objects.hash(architecture, os, serverVersion);
162 }
163
164 private static String computeTarXzArchitectureName() {
165 String architecture = EmbeddedUtil.OS_ARCH;
166 if (EmbeddedUtil.IS_ARCH_X86_64) {
167 architecture = "x86_64";
168 } else if (EmbeddedUtil.IS_ARCH_AARCH64) {
169 if (!PREFER_NATIVE && EmbeddedUtil.IS_OS_MAC) {
170
171 architecture = "x86_64";
172 } else {
173 architecture = "arm_64";
174 }
175 } else if (EmbeddedUtil.IS_ARCH_AARCH32) {
176 architecture = "arm_32";
177 }
178 return architecture;
179 }
180
181 private static String computeJarArchitectureName() {
182 String architecture = EmbeddedUtil.OS_ARCH;
183 if (EmbeddedUtil.IS_ARCH_X86_64) {
184 architecture = "amd64";
185 } else if (EmbeddedUtil.IS_ARCH_AARCH64) {
186 if (!PREFER_NATIVE && EmbeddedUtil.IS_OS_MAC) {
187
188 architecture = "amd64";
189 } else {
190 architecture = "arm64v8";
191 }
192 } else if (EmbeddedUtil.IS_ARCH_AARCH32) {
193 architecture = "arm32v7";
194 }
195 return architecture;
196 }
197
198 private static String computeOS() {
199 String os = EmbeddedUtil.OS_NAME;
200 if (EmbeddedUtil.IS_OS_LINUX) {
201 os = "linux";
202 } else if (EmbeddedUtil.IS_OS_MAC) {
203 os = "darwin";
204 } else if (EmbeddedUtil.IS_OS_WINDOWS) {
205 os = "windows";
206 }
207 return os;
208 }
209 }