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.checkArgument;
18 import static com.google.common.base.Preconditions.checkState;
19
20 import jakarta.annotation.Nonnull;
21 import java.io.File;
22 import java.io.IOException;
23 import java.net.ServerSocket;
24 import java.nio.file.Files;
25 import java.nio.file.Path;
26 import java.time.Duration;
27 import java.util.Comparator;
28 import java.util.Locale;
29 import java.util.Objects;
30 import java.util.concurrent.ThreadLocalRandom;
31 import java.util.stream.Stream;
32
33 import com.google.common.base.Joiner;
34 import com.google.common.base.Strings;
35 import com.google.common.collect.ImmutableList;
36
37 final class EmbeddedUtil {
38
39 static final String OS_NAME;
40 static final String OS_ARCH;
41
42 static final boolean IS_OS_WINDOWS;
43 static final boolean IS_OS_MAC;
44 static final boolean IS_OS_LINUX;
45 static final boolean IS_ALPINE_LINUX;
46
47 static final boolean IS_ARCH_X86_64;
48 static final boolean IS_ARCH_AARCH64;
49 static final boolean IS_ARCH_AARCH32;
50
51 private static final String ALPHANUM;
52 private static final String LOWERCASE;
53
54 static {
55 OS_NAME = getSystemProperty("os.name");
56 OS_ARCH = getSystemProperty("os.arch");
57
58 IS_OS_WINDOWS = getOsMatchesName("Windows");
59 IS_OS_LINUX = getOsMatchesName("Linux");
60 IS_OS_MAC = getOsMatchesName("Mac");
61
62 IS_ARCH_X86_64 = OS_ARCH.equals("x86_64") || OS_ARCH.equals("amd64");
63 IS_ARCH_AARCH64 = OS_ARCH.equals("aarch64");
64 IS_ARCH_AARCH32 = OS_ARCH.equals("aarch32") || OS_ARCH.equals("arm");
65
66
67 IS_ALPINE_LINUX = new File("/etc/alpine-release").exists();
68
69 String numbers = sequence('0', 10);
70 LOWERCASE = sequence('a', 26);
71 String uppercase = sequence('A', 26);
72 ALPHANUM = numbers + LOWERCASE + uppercase;
73 }
74
75 private EmbeddedUtil() {
76 throw new AssertionError("EmbeddedUtil can not be instantiated");
77 }
78
79 static File getWorkingDirectory() {
80 File parent = new File(System.getProperty("java.io.tmpdir"));
81
82 File workDir = new File(parent, "embedded-pg-" + Objects.requireNonNullElse(System.getProperty("user.name"), "unknown"));
83
84 ensureDirectory(workDir);
85
86 return workDir;
87 }
88
89 static void ensureDirectory(@Nonnull File workDir) {
90
91 long retryCount = 5;
92
93 while (retryCount > 0) {
94 if (workDir.exists()) {
95 break;
96 }
97 if (workDir.mkdirs()) {
98 break;
99 }
100 retryCount--;
101
102 try {
103 Thread.sleep(100 * (6 - retryCount));
104 } catch (InterruptedException e) {
105 Thread.currentThread().interrupt();
106 throw new IllegalStateException("Could not create working directory'" + workDir.getAbsolutePath() + "'", e);
107 }
108
109 }
110 if (retryCount == 0) {
111 throw new IllegalStateException("Could not create working directory '" + workDir.getAbsolutePath() + "' after 5 tries");
112 }
113
114 checkState(workDir.exists(), "'%s' does not exist!", workDir);
115 checkState(workDir.isDirectory(), "'%s' exists but is not a directory!", workDir);
116
117 if (!workDir.canWrite()) {
118 checkState(workDir.setWritable(true, false), "Could not make directory '%s' writeable!", workDir);
119 }
120
121 checkState(workDir.canWrite(), "'%s' is a directory but can not be written!", workDir);
122 }
123
124
125
126
127 static String getFileBaseName(final String fileName) {
128 if (fileName == null) {
129 return null;
130 }
131 failIfNullBytePresent(fileName);
132 final int index = indexOfLastSeparator(fileName);
133 return fileName.substring(index + 1);
134 }
135
136 private static void failIfNullBytePresent(final String path) {
137 final int len = path.length();
138 for (int i = 0; i < len; i++) {
139 checkArgument(path.charAt(i) != 0,
140 "Null byte present in file/path name.");
141 }
142 }
143
144 private static int indexOfLastSeparator(final String fileName) {
145 if (fileName == null) {
146 return -1;
147 }
148 final int lastUnixPos = fileName.lastIndexOf('/');
149 final int lastWindowsPos = fileName.lastIndexOf('\\');
150 return Math.max(lastUnixPos, lastWindowsPos);
151 }
152
153
154
155
156
157 static void rmdirs(File dir) throws IOException {
158 if (dir.exists() && dir.isDirectory()) {
159 try (Stream<Path> walk = Files.walk(dir.toPath())) {
160 walk.sorted(Comparator.reverseOrder())
161 .map(Path::toFile)
162 .forEach(File::delete);
163 }
164 }
165 }
166
167 static String formatDuration(Duration duration) {
168 long millis = duration.toMillis();
169 long hours;
170 long minutes;
171 long secs;
172 long ms;
173 ImmutableList.Builder<String> builder = ImmutableList.builder();
174 if (millis == 0) {
175 builder.add("0 ms");
176 } else {
177 long seconds = millis / 1000L;
178 hours = seconds / 3600L;
179 minutes = (seconds % 3600L) / 60L;
180 secs = (seconds % 60L);
181 ms = millis % 1000L;
182
183 if (hours != 0) {
184 builder.add(hours + " hours");
185 }
186 if (minutes != 0) {
187 builder.add(minutes + " minutes");
188 }
189 if (secs != 0) {
190 builder.add(secs + " seconds");
191 }
192 if (ms != 0) {
193 builder.add(ms + " ms");
194 }
195 }
196
197 return Joiner.on(' ').join(builder.build());
198 }
199
200 static int allocatePort() throws IOException {
201 try (ServerSocket socket = new ServerSocket(0)) {
202 while (!socket.isBound()) {
203 Thread.sleep(50);
204 }
205 return socket.getLocalPort();
206 } catch (InterruptedException e) {
207 Thread.currentThread().interrupt();
208 throw new IOException("Thread interrupted!", e);
209 }
210 }
211
212 static String randomAlphaNumeric(int length) {
213 return randomString(ALPHANUM, length);
214 }
215
216 static String randomLowercase(int length) {
217 return randomString(LOWERCASE, length);
218 }
219
220 private static String sequence(char start, int count) {
221 StringBuilder sb = new StringBuilder();
222 for (int i = 0; i < count; i++) {
223 sb.append((char) (start + i));
224 }
225 return sb.toString();
226 }
227
228 private static String randomString(String alphabet, int length) {
229 StringBuilder sb = new StringBuilder();
230 for (int i = 0; i < length; i++) {
231 int random = ThreadLocalRandom.current().nextInt(alphabet.length());
232 sb.append(alphabet.charAt(random));
233 }
234 return sb.toString();
235 }
236
237 private static String getSystemProperty(String propertyName) {
238 try {
239 return Strings.nullToEmpty(System.getProperty(propertyName, ""));
240 } catch (SecurityException e) {
241 return "<unknown>";
242 }
243 }
244
245 private static boolean getOsMatchesName(final String osNamePrefix) {
246 return OS_NAME.toLowerCase(Locale.ROOT).startsWith(osNamePrefix.toLowerCase(Locale.ROOT));
247 }
248 }