-
Notifications
You must be signed in to change notification settings - Fork 185
Windows support #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Windows support #13
Changes from 11 commits
d617516
263d5ed
01b43bf
7ce238d
6472d0f
53686d0
85f8564
9378e7c
d4ac576
98b3297
5c6804a
ffb7ffd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,17 +13,41 @@ | |
*/ | ||
package com.opentable.db.postgres.embedded; | ||
|
||
import static com.google.common.base.MoreObjects.firstNonNull; | ||
import com.google.common.base.Preconditions; | ||
import com.google.common.base.Throwables; | ||
import com.google.common.collect.ImmutableMap; | ||
import com.google.common.collect.Lists; | ||
import com.google.common.collect.Maps; | ||
import com.google.common.io.Closeables; | ||
import com.google.common.io.Files; | ||
import org.apache.commons.codec.binary.Hex; | ||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry; | ||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; | ||
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; | ||
import org.apache.commons.io.FileUtils; | ||
import org.apache.commons.io.IOUtils; | ||
import org.apache.commons.lang3.SystemUtils; | ||
import org.apache.commons.lang3.time.StopWatch; | ||
import org.postgresql.ds.PGSimpleDataSource; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import javax.sql.DataSource; | ||
import java.io.BufferedInputStream; | ||
import java.io.ByteArrayInputStream; | ||
import java.io.ByteArrayOutputStream; | ||
import java.io.Closeable; | ||
import java.io.File; | ||
import java.io.FileInputStream; | ||
import java.io.FileOutputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.net.ServerSocket; | ||
import java.net.UnknownHostException; | ||
import java.nio.channels.FileLock; | ||
import java.nio.channels.OverlappingFileLockException; | ||
import java.nio.file.FileSystems; | ||
import java.nio.file.Path; | ||
import java.security.DigestInputStream; | ||
import java.security.MessageDigest; | ||
import java.security.NoSuchAlgorithmException; | ||
|
@@ -42,25 +66,9 @@ | |
import java.util.concurrent.locks.Lock; | ||
import java.util.concurrent.locks.ReentrantLock; | ||
|
||
import javax.sql.DataSource; | ||
|
||
import com.google.common.base.Preconditions; | ||
import com.google.common.base.Throwables; | ||
import com.google.common.collect.ImmutableMap; | ||
import com.google.common.collect.Lists; | ||
import com.google.common.collect.Maps; | ||
import com.google.common.io.Closeables; | ||
import com.google.common.io.Files; | ||
|
||
import org.apache.commons.codec.binary.Hex; | ||
import org.apache.commons.io.FileUtils; | ||
import org.apache.commons.io.IOUtils; | ||
import org.apache.commons.lang3.time.StopWatch; | ||
import org.postgresql.ds.PGSimpleDataSource; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import static com.google.common.base.MoreObjects.firstNonNull; | ||
|
||
public class EmbeddedPostgres implements Closeable | ||
public class EmbeddedPostgres implements AutoCloseable, Closeable | ||
{ | ||
private static final Logger LOG = LoggerFactory.getLogger(EmbeddedPostgres.class); | ||
private static final String JDBC_FORMAT = "jdbc:postgresql://localhost:%s/%s?user=%s"; | ||
|
@@ -342,7 +350,8 @@ private void cleanOldDataDirectories(File parentDirectory) | |
|
||
private String pgBin(String binaryName) | ||
{ | ||
return new File(pgDir, "bin/" + binaryName).getPath(); | ||
final String extension = SystemUtils.IS_OS_WINDOWS ? ".exe" : ""; | ||
return new File(pgDir, "bin/" + binaryName + extension).getPath(); | ||
} | ||
|
||
public static EmbeddedPostgres start() throws IOException | ||
|
@@ -424,7 +433,7 @@ private static List<String> system(String... command) | |
} | ||
} | ||
|
||
private void mkdirs(File dir) | ||
private static void mkdirs(File dir) | ||
{ | ||
Preconditions.checkState(dir.mkdirs() || (dir.isDirectory() && dir.exists()), // NOPMD | ||
"could not create %s", dir); | ||
|
@@ -433,15 +442,91 @@ private void mkdirs(File dir) | |
private static final AtomicReference<File> BINARY_DIR = new AtomicReference<>(); | ||
private static final Lock PREPARE_BINARIES_LOCK = new ReentrantLock(); | ||
|
||
private File prepareBinaries(PgBinaryResolver pgBinaryResolver) { | ||
/** | ||
* Get current operating system string. The string is used in the appropriate postgres binary name. | ||
* | ||
* @return Current operating system string. | ||
*/ | ||
private static String getOS(){ | ||
return SystemUtils.IS_OS_WINDOWS ? "Windows" : system("uname", "-s").get(0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this just use the appropriate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't want to redesign existing functionality / naming and I am not able to test what Apple returns. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will handle that :) |
||
} | ||
|
||
/** | ||
* Get the machine architecture string. The string is used in the appropriate postgres binary name. | ||
* | ||
* @return Current machine architecture string. | ||
*/ | ||
private static String getArchitecture(){ | ||
if (!SystemUtils.IS_OS_WINDOWS) { | ||
return system("uname", "-m").get(0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto here, should we just use the property? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same here, I cannot test Apple and don't want to break it. |
||
} | ||
|
||
return "amd64".equals(SystemUtils.OS_ARCH) || SystemUtils.OS_ARCH == null ? "x86_64" : SystemUtils.OS_ARCH; | ||
} | ||
|
||
/** | ||
* Unpack archive compressed by tar with bzip2 compression. By default system tar is used (faster). If not found, then the | ||
* java implementation takes place. | ||
* | ||
* @param tbzPath The archive path. | ||
* @param targetDir The directory to extract the content to. | ||
*/ | ||
private static void extractTbz(final String tbzPath, final String targetDir) throws IOException { | ||
try { | ||
system("tar", "-x", "-f", tbzPath, "-C", targetDir); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we are going to use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, I removed it but then I returned system tar, because commons-compress way does not allow to set file modes easily and I am not able to test on Mac (on Linux I was able to test and found issues with file modes along with issues with symlinks. I have no idea what may happen on Mac.). |
||
} catch (final Exception e) { | ||
try ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if this exception is because of a corrupt download, or truncated file, or something? It'd get swallowed. Probably should just get rid of the fallback and use commons-compress directly and remove the |
||
final FileInputStream fin = new FileInputStream(tbzPath); | ||
final BufferedInputStream in = new BufferedInputStream(fin); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. replaced |
||
final ByteArrayOutputStream tarOut = new ByteArrayOutputStream(); | ||
final BZip2CompressorInputStream bzIn = new BZip2CompressorInputStream(in) | ||
) { | ||
final byte[] buffer = new byte[4096]; | ||
int n; | ||
while (-1 != (n = bzIn.read(buffer))) { | ||
tarOut.write(buffer, 0, n); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is the decompression done to a byte array input stream? Why not just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. simplified |
||
} | ||
|
||
final TarArchiveInputStream tarIn = new TarArchiveInputStream(new ByteArrayInputStream(tarOut.toByteArray())); | ||
TarArchiveEntry entry; | ||
String individualFile; | ||
int offset; | ||
FileOutputStream outputFile; | ||
|
||
while ((entry = tarIn.getNextTarEntry()) != null) { | ||
individualFile = entry.getName(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need any protection against There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm... I have no idea. I have tested it on existing archives and there is no such issue. |
||
final File fsObject = new File(targetDir + "/" + individualFile); | ||
if (entry.isSymbolicLink()) { | ||
final Path target = FileSystems.getDefault().getPath(entry.getLinkName()); | ||
java.nio.file.Files.createSymbolicLink(fsObject.toPath(), target); | ||
} else if (entry.isFile()) { | ||
final byte[] content = new byte[(int) entry.getSize()]; | ||
offset = 0; | ||
final int read = tarIn.read(content, offset, content.length - offset); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this guaranteed to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe it cannot be short read in our case. |
||
Preconditions.checkState(read != -1, "could not read %s", individualFile); | ||
mkdirs(fsObject.getParentFile()); | ||
outputFile = new FileOutputStream(fsObject); | ||
IOUtils.write(content, outputFile); | ||
outputFile.close(); | ||
} else if (entry.isDirectory()) { | ||
mkdirs(fsObject); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. } else {
throw new UnsupportedOperationException();
} ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added exception with entry name |
||
} | ||
|
||
tarIn.close(); | ||
} | ||
} | ||
} | ||
|
||
private static File prepareBinaries(PgBinaryResolver pgBinaryResolver) { | ||
PREPARE_BINARIES_LOCK.lock(); | ||
try { | ||
if(BINARY_DIR.get() != null) { | ||
return BINARY_DIR.get(); | ||
} | ||
|
||
String system = system("uname", "-s").get(0); | ||
String machineHardware = system("uname", "-m").get(0); | ||
final String system = getOS(); | ||
final String machineHardware = getArchitecture(); | ||
|
||
LOG.info("Detected a {} {} system", system, machineHardware); | ||
File pgDir; | ||
|
@@ -475,10 +560,10 @@ private File prepareBinaries(PgBinaryResolver pgBinaryResolver) { | |
try { | ||
Preconditions.checkState(!pgDirExists.exists(), "unpack lock acquired but .exists file is present."); | ||
LOG.info("Extracting Postgres..."); | ||
system("tar", "-x", "-f", pgTbz.getPath(), "-C", pgDir.getPath()); | ||
extractTbz(pgTbz.getPath(), pgDir.getPath()); | ||
Files.touch(pgDirExists); | ||
} finally { | ||
Preconditions.checkState(unpackLockFile.delete(), "could not remove lock file %s", unpackLockFile.getAbsolutePath()); | ||
} catch (Exception e) { | ||
LOG.error(e.getMessage()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
} | ||
} else { | ||
// the other guy is unpacking for us. | ||
|
@@ -488,6 +573,10 @@ private File prepareBinaries(PgBinaryResolver pgBinaryResolver) { | |
} | ||
Preconditions.checkState(pgDirExists.exists(), "Waited 60 seconds for postgres to be unpacked but it never finished!"); | ||
} | ||
} finally { | ||
if (unpackLockFile.exists()) { | ||
Preconditions.checkState(unpackLockFile.delete(), "could not remove lock file %s", unpackLockFile.getAbsolutePath()); | ||
} | ||
} | ||
} | ||
} catch (final IOException | NoSuchAlgorithmException e) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Closeable
extendsAutoCloseable
so this declaration is redundant, yeah?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removing