Skip to content
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

Improve command-line argument for extracting resources #728

Merged
merged 5 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 149 additions & 0 deletions server/embedded/src/org/labkey/embedded/EmbeddedExtractor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package org.labkey.embedded;

import org.labkey.bootstrap.ConfigException;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class EmbeddedExtractor
{
private static final int BUFFER_SIZE = 1024 * 64;

private void extractExecutableJar(File jarFilePath, File destDirectory, boolean remotePipeline)
{
try
{
try (JarFile jar = new JarFile(jarFilePath))
{
boolean foundDistributionZip = false;
var entries = jar.entries();
while (entries.hasMoreElements())
{
var entry = entries.nextElement();
var entryName = entry.getName();

if ("labkey/distribution.zip".equals(entryName))
{
foundDistributionZip = true;
try (var distInputStream = jar.getInputStream(entry))
{
extractZip(distInputStream, destDirectory);
}
}
if (remotePipeline)
{
if (entry.getName().contains("labkeyBootstrap") && entry.getName().toLowerCase().endsWith(".jar"))
{
try (var in = jar.getInputStream(entry))
{
extractFile(in, new File(destDirectory, "labkeyBootstrap.jar"));
}
}
if (entry.getName().contains("tomcat-servlet-api") && entry.getName().toLowerCase().endsWith(".jar"))
{
File pipelineLib = new File(destDirectory, "pipeline-lib");
if (!pipelineLib.exists())
{
if (!pipelineLib.mkdirs())
{
throw new ConfigException("Failed to create directory " + pipelineLib + " Please check file system permissions");
}
}
try (var in = jar.getInputStream(entry))
{
extractFile(in, new File(pipelineLib, "servletApi.jar"));
}
}
}
}

if (!foundDistributionZip)
{
throw new ConfigException("Unable to find distribution zip required to run LabKey Server.");
}
}
}
catch (IOException | ConfigException e)
{
throw new RuntimeException(e);
}
}

public void extractExecutableJarFromDir(File currentDir, File destDir, boolean remotePipeline) throws ConfigException
{
File[] files = currentDir.listFiles(file -> {
String name = file.getName().toLowerCase();
return name.endsWith(".jar") && !name.contains("labkeybootstrap");
});

if (files == null)
{
throw new ConfigException("Executable jar not found.");
}

// only 1 jar should be there
if (files.length == 1)
{
extractExecutableJar(files[0], destDir, remotePipeline);
}
else
{
throw new ConfigException("Multiple jars found - " + Arrays.asList(files) + ". Must provide only one jar.");
}
}

private void extractZip(InputStream zipInputStream, File destDir) throws IOException
{
//noinspection SSBasedInspection
if (!destDir.exists() && !destDir.mkdirs())
{
throw new IOException("Failed to create directory " + destDir + " - please check file system permissions");
}
try (ZipInputStream zipIn = new ZipInputStream(zipInputStream))
{
ZipEntry entry = zipIn.getNextEntry();
// iterates over entries in the zip file
while (entry != null)
{
File filePath = new File(destDir, entry.getName());
if (!entry.isDirectory())
{
// if the entry is a file, extracts it
extractFile(zipIn, filePath);
}
else
{
// if the entry is a directory, make the directory
//noinspection SSBasedInspection
if (!filePath.exists() && !filePath.mkdirs())
{
throw new IOException("Failed to create directory " + filePath + " - please check file system permissions");
}
}
zipIn.closeEntry();
entry = zipIn.getNextEntry();
}
}
}

private static void extractFile(InputStream zipIn, File filePath) throws IOException
{
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath)))
{
byte[] bytesIn = new byte[BUFFER_SIZE];
int read;
while ((read = zipIn.read(bytesIn)) != -1)
{
bos.write(bytesIn, 0, read);
}
}
}

}
8 changes: 8 additions & 0 deletions server/embedded/src/org/labkey/embedded/LabKeyServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.annotation.Validated;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
Expand All @@ -37,6 +38,13 @@ public class LabKeyServer

public static void main(String[] args)
{
if (args.length > 0 && args[0].equalsIgnoreCase("-extract"))
{
File currentDir = new File("").getAbsoluteFile();
new EmbeddedExtractor().extractExecutableJarFromDir(currentDir, currentDir, true);
return;
}

// Issue 40038: Ride-or-die Mode - default to shutting down by default in embedded deployment scenario
if (System.getProperty(TERMINATE_ON_STARTUP_FAILURE) == null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,10 @@
import org.springframework.boot.web.servlet.ServletContextInitializer;

import javax.sql.DataSource;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import static org.labkey.embedded.LabKeyServer.SERVER_GUID_PARAMETER_NAME;
import static org.labkey.embedded.LabKeyServer.SERVER_SSL_KEYSTORE;
Expand All @@ -36,8 +27,6 @@ class LabKeyTomcatServletWebServerFactory extends TomcatServletWebServerFactory
private static final Log LOG = LogFactory.getLog(LabKeyTomcatServletWebServerFactory.class);
private final LabKeyServer _server;

private static final int BUFFER_SIZE = 4096;

public LabKeyTomcatServletWebServerFactory(LabKeyServer server)
{
_server = server;
Expand Down Expand Up @@ -70,26 +59,25 @@ protected TomcatWebServer getTomcatWebServer(Tomcat tomcat)

// for development, point to the local deploy/labkeyWebapp directory in configs/application.properties
boolean webAppLocationPresent = contextProperties.getWebAppLocation() != null;
var webAppLocation = "";
File webAppLocation;

try
{
if (!webAppLocationPresent)
{
final var currentPath = new File("").getAbsolutePath();
var destDirectory = currentPath + "/server";
webAppLocation = destDirectory + "/labkeywebapp";
boolean extracted = new File(webAppLocation).exists();
String jarFilePath = getExecutableJar(currentPath);
final var currentPath = new File("");
var destDirectory = new File(currentPath, "server");
webAppLocation = new File(destDirectory, "labkeywebapp");

if (!extracted)
if (!webAppLocation.exists())
{
extractExecutableJar(destDirectory, jarFilePath);
EmbeddedExtractor extractor = new EmbeddedExtractor();
extractor.extractExecutableJarFromDir(currentPath, destDirectory, false);
}
}
else
{
webAppLocation = contextProperties.getWebAppLocation();
webAppLocation = new File(contextProperties.getWebAppLocation());
}

// Turn off the default web.xml behavior so that we don't stomp over customized values
Expand All @@ -104,7 +92,7 @@ protected TomcatWebServer getTomcatWebServer(Tomcat tomcat)

// Spring Boot's webapp is being deployed to the root. We have to deploy elsewhere in this initial
// call, but can immediately swap it with the desired place
StandardContext context = (StandardContext) tomcat.addWebapp("/labkey", webAppLocation);
StandardContext context = (StandardContext) tomcat.addWebapp("/labkey", webAppLocation.getAbsolutePath());
// set the root path to the context explicitly
context.setPath(contextProperties.getContextPath());

Expand Down Expand Up @@ -375,119 +363,4 @@ private ContextResource getMailResource()

return mailResource;
}

private void extractExecutableJar(String destDirectory, String jarFilePath)
{
try
{
try (JarFile jar = new JarFile(jarFilePath))
{
boolean foundDistributionZip = false;
var entries = jar.entries();
while (entries.hasMoreElements())
{
var entry = entries.nextElement();
var entryName = entry.getName();

if ("labkey/distribution.zip".equals(entryName))
{
foundDistributionZip = true;
try (var distInputStream = jar.getInputStream(entry))
{
extractZip(distInputStream, destDirectory);
}
}
}

if (!foundDistributionZip)
{
throw new ConfigException("Unable to find distribution zip required to run LabKey server.");
}
}
}
catch (IOException | ConfigException e)
{
throw new RuntimeException(e);
}
}

private static String getExecutableJar(String currentPath) throws ConfigException
{
File currentDir = new File(currentPath);
List<String> jarsPresent = new ArrayList<>();

File[] files = currentDir.listFiles();
if (files != null)
{
for (File file : files)
{
if (file.getName().toLowerCase().endsWith(".jar"))
{
jarsPresent.add(file.getName());
}
}
}

if (jarsPresent.isEmpty())
{
throw new ConfigException("Executable jar not found.");
}

// only 1 jar should be there
if (jarsPresent.size() == 1)
{
return jarsPresent.get(0);
}

throw new ConfigException("Multiple jars found - " + jarsPresent + ". Must provide only one jar.");
}

private void extractZip(InputStream zipInputStream, String destDirectory) throws IOException
{
File destDir = new File(destDirectory);
//noinspection SSBasedInspection
if (!destDir.exists() && !destDir.mkdirs())
{
throw new IOException("Failed to create directory " + destDir + " - please check file system permissions");
}
try (ZipInputStream zipIn = new ZipInputStream(zipInputStream))
{
ZipEntry entry = zipIn.getNextEntry();
// iterates over entries in the zip file
while (entry != null)
{
String filePath = destDirectory + File.separator + entry.getName();
if (!entry.isDirectory())
{
// if the entry is a file, extracts it
extractFile(zipIn, filePath);
}
else
{
// if the entry is a directory, make the directory
File dir = new File(filePath);
//noinspection SSBasedInspection
if (!dir.exists() && !dir.mkdirs())
{
throw new IOException("Failed to create directory " + dir + " - please check file system permissions");
}
}
zipIn.closeEntry();
entry = zipIn.getNextEntry();
}
}
}

private static void extractFile(ZipInputStream zipIn, String filePath) throws IOException
{
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath)))
{
byte[] bytesIn = new byte[BUFFER_SIZE];
int read;
while ((read = zipIn.read(bytesIn)) != -1)
{
bos.write(bytesIn, 0, read);
}
}
}
}