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