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

Support additional context paths #687

Merged
merged 3 commits into from
Jan 25, 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
14 changes: 14 additions & 0 deletions server/configs/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ context.validationQuery[0]=SELECT 1

#useLocalBuild#context.webAppLocation=@@pathToServer@@/build/deploy/labkeyWebapp
context.encryptionKey=@@encryptionKey@@

# By default, we'll deploy to the root context path. However, some servers have historically used /labkey or even /cpas
#context.contextPath=/labkey

# Using a legacy context path provides backwards compatibility with old deployments. A typical use case would be to
# deploy to the root context (the default) and configure /labkey as the legacy path. GETs will be redirected, and
# non-GETs will be handled server-side via a servlet forward.
#context.legacyContextPath=/labkey

# Other webapps to be deployed, most commonly to deliver a set of static files. Additional
# webapps can be specified via additional indices. docBase should point at the root of the webapp's content
#webapps.contextPath[0]=/anotherWebapp
#webapps.docBase[0]=/my/webapp/path

#context.oldEncryptionKey=
#context.requiredModules=
#context.pipelineConfig=/path/to/pipeline/config/dir
Expand Down
81 changes: 80 additions & 1 deletion server/embedded/src/org/labkey/embedded/LabKeyServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ public JmsProperties jmsSource()
return new JmsProperties();
}

@Bean
public WebappProperties additionalWebappSource()
{
return new WebappProperties();
}

@Bean
public CSPFilterProperties cspSource()
{
Expand Down Expand Up @@ -156,7 +162,7 @@ protected TomcatWebServer getTomcatWebServer(Tomcat tomcat)
}

// set the root path to the context explicitly
context.setPath("");
context.setPath(contextProperties.getContextPath());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any need to do trimming on this value and the legacyContextPath value? Perhaps in the setter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be fine to do, but we don't trim any of the other properties so I'm inclined to leave as-is. From a quick test, the properties file parser automatically trims leading spaces for the value (after the =) but not trailing spaces.


// Push the JDBC connection for the primary DB into the context so that the LabKey webapp finds them
getDataSourceResources(contextProperties, context).forEach(contextResource -> context.getNamingResources().addResource(contextResource));
Expand All @@ -183,6 +189,14 @@ protected TomcatWebServer getTomcatWebServer(Tomcat tomcat)
context.addParameter("OldEncryptionKey", contextProperties.getOldEncryptionKey());
}

if (contextProperties.getLegacyContextPath() != null)
{
if (contextProperties.getContextPath() != null && !contextProperties.getContextPath().isEmpty() && !contextProperties.getContextPath().equals("/"))
{
throw new IllegalArgumentException("contextPath.legacyContextPath is only intended for use when deploying the LabKey application to the root context path. Please update application.properties.");
}
context.addParameter("legacyContextPath", contextProperties.getLegacyContextPath());
}
if (contextProperties.getRequiredModules() != null)
{
context.addParameter("requiredModules", contextProperties.getRequiredModules());
Expand Down Expand Up @@ -222,6 +236,18 @@ protected TomcatWebServer getTomcatWebServer(Tomcat tomcat)
configureJsonAccessLogging(tomcat, logConfig);
}

WebappProperties additionalWebapps = additionalWebappSource();
if (additionalWebapps.getContextPath().size() != additionalWebapps.getDocBase().size())
{
throw new IllegalArgumentException("Additional webapps must have paired contextPath and docBase properties");
}
for (int i = 0; i < additionalWebapps.getContextPath().size(); i++)
{
String contextPath = additionalWebapps.getContextPath().get(i);
String docBase = additionalWebapps.getDocBase().get(i);
tomcat.addWebapp(contextPath, docBase);
}

return super.getTomcatWebServer(tomcat);
}

Expand Down Expand Up @@ -579,6 +605,35 @@ public void setConditionUnless(String conditionUnless)
}
}

@Configuration
@ConfigurationProperties("webapps")
public static class WebappProperties
{
private List<String> contextPath = new ArrayList<>();

private List<String> docBase = new ArrayList<>();

public List<String> getContextPath()
{
return contextPath;
}

public void setContextPath(List<String> contextPath)
{
this.contextPath = contextPath;
}

public List<String> getDocBase()
{
return docBase;
}

public void setDocBase(List<String> docBase)
{
this.docBase = docBase;
}
}

@Validated
@Configuration
@ConfigurationProperties("context")
Expand All @@ -600,6 +655,10 @@ public static class ContextProperties
@NotNull (message = "Must provide encryptionKey")
private String encryptionKey;
private String oldEncryptionKey;
private String legacyContextPath;

// Default to deploying to the root context path
private String contextPath = "";
private String pipelineConfig;
private String requiredModules;
private boolean bypass2FA = false;
Expand Down Expand Up @@ -702,6 +761,26 @@ public void setOldEncryptionKey(String oldEncryptionKey)
this.oldEncryptionKey = oldEncryptionKey;
}

public String getLegacyContextPath()
{
return legacyContextPath;
}

public void setLegacyContextPath(String legacyContextPath)
{
this.legacyContextPath = legacyContextPath;
}

public String getContextPath()
{
return contextPath;
}

public void setContextPath(String contextPath)
{
this.contextPath = contextPath;
}

public String getPipelineConfig()
{
return pipelineConfig;
Expand Down