-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Issue #12990 - Introduce static-deploy
module
#12998
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
Changes from all commits
d72b287
a598469
c5cb76d
81c1f51
3c7156b
cd2a996
6e9adbb
fa2e720
17ea7ed
7351898
518ea13
499f5b0
5b80a94
60065cc
eb2c5dd
0ee1529
3d5c525
0329896
1f49310
cadf0a5
8149809
532b422
db8f04d
f0a2f3b
b159daf
99174e4
660ae3f
aaab35f
860415b
e3c7ce4
65aaca2
40f0996
6ca1507
32c5507
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 | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -31,8 +31,11 @@ | |||||||||
import java.util.Objects; | ||||||||||
import java.util.Properties; | ||||||||||
import java.util.Set; | ||||||||||
import java.util.TreeSet; | ||||||||||
import java.util.concurrent.CopyOnWriteArrayList; | ||||||||||
import java.util.function.Predicate; | ||||||||||
import java.util.regex.Matcher; | ||||||||||
import java.util.regex.Pattern; | ||||||||||
import java.util.stream.Collectors; | ||||||||||
import java.util.stream.Stream; | ||||||||||
|
||||||||||
|
@@ -128,12 +131,40 @@ public class DeploymentScanner extends ContainerLifeCycle implements Scanner.Bul | |||||||||
// old attributes prefix, now stripped. | ||||||||||
private static final String ATTRIBUTE_PREFIX = "jetty.deploy.attribute."; | ||||||||||
|
||||||||||
private static final Pattern EE_ENVIRONMENT_NAME_PATTERN = Pattern.compile("ee(\\d+)"); | ||||||||||
|
||||||||||
/** | ||||||||||
* A comparator that ranks names matching EE_ENVIRONMENT_NAME_PATTERN higher than other names, | ||||||||||
* EE names are compared by EE number, otherwise simple name comparison is used. | ||||||||||
*/ | ||||||||||
protected static final Comparator<String> ENVIRONMENT_COMPARATOR = (e1, e2) -> | ||||||||||
{ | ||||||||||
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. This comparator will generate a lot of matchers, so perhaps take the returns where you can:
Suggested change
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. Better yet, why not just create an Index that is case insensitive and maps all the known environment names to an integer. Index<Integer> environmentOrdinals = new Index.Builder<Integer>()
.caseSensitive(false)
.with("static", 1)
.with("core", 2)
.with("ee9", 9)
.with("ee10", 10)
.with("ee11", 11)
.build(); 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. In a conversation with @sbordet and @lorban this will be fixed in a followup PR. I have an approach that will push the weight of the tracked environments (the ones allowed to be deployed to) via a change to the That way the weights are not hardcoded, and can be adjusted by any user, and even add new environments (with their own weights) without the need to modify code. |
||||||||||
Matcher m1 = EE_ENVIRONMENT_NAME_PATTERN.matcher(e1); | ||||||||||
Matcher m2 = EE_ENVIRONMENT_NAME_PATTERN.matcher(e2); | ||||||||||
|
||||||||||
if (m1.matches()) | ||||||||||
{ | ||||||||||
if (m2.matches()) | ||||||||||
{ | ||||||||||
int n1 = Integer.parseInt(m1.group(1)); | ||||||||||
int n2 = Integer.parseInt(m2.group(1)); | ||||||||||
return Integer.compare(n2, n1); | ||||||||||
} | ||||||||||
return -1; | ||||||||||
} | ||||||||||
if (m2.matches()) | ||||||||||
return 1; | ||||||||||
|
||||||||||
return e1.compareTo(e2); | ||||||||||
}; | ||||||||||
|
||||||||||
private final Server server; | ||||||||||
private final FilenameFilter filenameFilter; | ||||||||||
private final List<Path> monitoredDirs = new CopyOnWriteArrayList<>(); | ||||||||||
private final ContextHandlerFactory contextHandlerFactory; | ||||||||||
private final Map<String, PathsApp> trackedApps = new HashMap<>(); | ||||||||||
private final Map<String, Attributes> environmentAttributesMap = new HashMap<>(); | ||||||||||
private Set<String> trackedEnvironments = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); | ||||||||||
|
||||||||||
private Deployer deployer; | ||||||||||
private Comparator<DeployAction> actionComparator = new DeployActionComparator(); | ||||||||||
|
@@ -293,7 +324,13 @@ void addScannerListener(Scanner.Listener listener) | |||||||||
*/ | ||||||||||
public EnvironmentConfig configureEnvironment(String name) | ||||||||||
{ | ||||||||||
return new EnvironmentConfig(Environment.get(name)); | ||||||||||
Environment environment = Environment.get(name); | ||||||||||
// Check to make sure that the Environment was created before jetty-deploy is involved. | ||||||||||
// This is to ensure that the Environment ClassLoader is setup properly. | ||||||||||
if (environment == null) | ||||||||||
throw new IllegalStateException("Environment [" + name + "] does not exist."); | ||||||||||
trackedEnvironments.add(name); | ||||||||||
return new EnvironmentConfig(environment); | ||||||||||
} | ||||||||||
|
||||||||||
/** | ||||||||||
|
@@ -314,8 +351,7 @@ public void setActionComparator(Comparator<DeployAction> actionComparator) | |||||||||
* do not declare the {@link Environment} that they belong to. | ||||||||||
* | ||||||||||
* <p> | ||||||||||
* Falls back to {@link Environment#getAll()} list, and returns | ||||||||||
* the first name returned after sorting with {@link Deployable#ENVIRONMENT_COMPARATOR} | ||||||||||
* Only uses environments that have been previously configured with {@link #configureEnvironment(String)} | ||||||||||
* </p> | ||||||||||
* | ||||||||||
* @return the default environment name. | ||||||||||
|
@@ -324,9 +360,8 @@ public String getDefaultEnvironmentName() | |||||||||
{ | ||||||||||
if (defaultEnvironmentName == null) | ||||||||||
{ | ||||||||||
return Environment.getAll().stream() | ||||||||||
.map(Environment::getName) | ||||||||||
.max(Deployable.ENVIRONMENT_COMPARATOR) | ||||||||||
return trackedEnvironments.stream() | ||||||||||
.min(ENVIRONMENT_COMPARATOR) | ||||||||||
.orElse(null); | ||||||||||
} | ||||||||||
return defaultEnvironmentName; | ||||||||||
|
@@ -813,21 +848,26 @@ protected void performActions(List<DeployAction> actions) | |||||||||
app.loadProperties(); | ||||||||||
|
||||||||||
// Ensure Environment name is set | ||||||||||
String appEnvironment = app.getEnvironmentName(); | ||||||||||
if (StringUtil.isBlank(appEnvironment)) | ||||||||||
appEnvironment = getDefaultEnvironmentName(); | ||||||||||
app.setEnvironment(Environment.get(appEnvironment)); | ||||||||||
String envName = app.getEnvironmentName(); | ||||||||||
if (StringUtil.isBlank(envName)) | ||||||||||
envName = getDefaultEnvironmentName(); | ||||||||||
Environment env = Environment.get(envName); | ||||||||||
|
||||||||||
if (env == null || !trackedEnvironments.contains(envName)) | ||||||||||
throw new IllegalArgumentException("Unable to deploy %s to environment %s. Available Environments to deploy to %s" | ||||||||||
.formatted(app.name, envName, trackedEnvironments.stream().sorted(ENVIRONMENT_COMPARATOR) | ||||||||||
.collect(Collectors.joining(", ", "[", "]")))); | ||||||||||
|
||||||||||
// Create a new Attributes layer for the app deployment, which is the | ||||||||||
// combination of layered Environment Attributes with app Attributes overlaying them. | ||||||||||
Attributes envAttributes = environmentAttributesMap.get(appEnvironment); | ||||||||||
Attributes envAttributes = environmentAttributesMap.get(envName); | ||||||||||
Attributes deployAttributes = envAttributes == null ? app.getAttributes() : new Attributes.Layer(envAttributes, app.getAttributes()); | ||||||||||
|
||||||||||
// Create the Context Handler | ||||||||||
Path mainPath = app.getMainPath(); | ||||||||||
if (mainPath == null) | ||||||||||
throw new IllegalStateException("Unable to create ContextHandler for app with no main path defined: " + app); | ||||||||||
ContextHandler contextHandler = contextHandlerFactory.newContextHandler(server, app.getEnvironment(), mainPath, app.getPaths().keySet(), deployAttributes); | ||||||||||
ContextHandler contextHandler = contextHandlerFactory.newContextHandler(server, env, mainPath, app.getPaths().keySet(), deployAttributes); | ||||||||||
app.setContextHandler(contextHandler); | ||||||||||
|
||||||||||
// Introduce the ContextHandler to the Deployer | ||||||||||
|
@@ -844,21 +884,26 @@ protected void performActions(List<DeployAction> actions) | |||||||||
app.loadProperties(); | ||||||||||
|
||||||||||
// Ensure Environment name is set | ||||||||||
String appEnvironment = app.getEnvironmentName(); | ||||||||||
if (StringUtil.isBlank(appEnvironment)) | ||||||||||
appEnvironment = getDefaultEnvironmentName(); | ||||||||||
app.setEnvironment(Environment.get(appEnvironment)); | ||||||||||
String envName = app.getEnvironmentName(); | ||||||||||
if (StringUtil.isBlank(envName)) | ||||||||||
envName = getDefaultEnvironmentName(); | ||||||||||
Environment env = Environment.get(envName); | ||||||||||
|
||||||||||
if (env == null || !trackedEnvironments.contains(envName)) | ||||||||||
throw new IllegalArgumentException("Unable to deploy app [%s] to environment [%s]. Available Environments to deploy to %s" | ||||||||||
.formatted(app.name, envName, trackedEnvironments.stream().sorted(ENVIRONMENT_COMPARATOR) | ||||||||||
.collect(Collectors.joining(", ", "[", "]")))); | ||||||||||
|
||||||||||
// Create a new Attributes layer for the app deployment, which is the | ||||||||||
// combination of layered Environment Attributes with app Attributes overlaying them. | ||||||||||
Attributes envAttributes = environmentAttributesMap.get(appEnvironment); | ||||||||||
Attributes envAttributes = environmentAttributesMap.get(envName); | ||||||||||
Attributes deployAttributes = envAttributes == null ? app.getAttributes() : new Attributes.Layer(envAttributes, app.getAttributes()); | ||||||||||
|
||||||||||
// Create the Context Handler | ||||||||||
Path mainPath = app.getMainPath(); | ||||||||||
if (mainPath == null) | ||||||||||
throw new IllegalStateException("Unable to create ContextHandler for app with no main path defined: " + app); | ||||||||||
ContextHandler contextHandler = contextHandlerFactory.newContextHandler(server, app.getEnvironment(), mainPath, app.getPaths().keySet(), deployAttributes); | ||||||||||
ContextHandler contextHandler = contextHandlerFactory.newContextHandler(server, env, mainPath, app.getPaths().keySet(), deployAttributes); | ||||||||||
app.setContextHandler(contextHandler); | ||||||||||
|
||||||||||
// Introduce the ContextHandler to the Deployer | ||||||||||
|
@@ -1341,23 +1386,14 @@ public void setContextHandler(ContextHandler contextHandler) | |||||||||
this.contextHandler = contextHandler; | ||||||||||
} | ||||||||||
|
||||||||||
public Environment getEnvironment() | ||||||||||
{ | ||||||||||
return (Environment)getAttributes().getAttribute(ContextHandlerFactory.ENVIRONMENT_ATTRIBUTE); | ||||||||||
} | ||||||||||
|
||||||||||
public void setEnvironment(Environment env) | ||||||||||
{ | ||||||||||
getAttributes().setAttribute(ContextHandlerFactory.ENVIRONMENT_ATTRIBUTE, env); | ||||||||||
} | ||||||||||
|
||||||||||
public String getEnvironmentName() | ||||||||||
{ | ||||||||||
Environment env = getEnvironment(); | ||||||||||
if (env == null) | ||||||||||
return ""; | ||||||||||
else | ||||||||||
Object obj = this.attributes.getAttribute(ContextHandlerFactory.ENVIRONMENT_ATTRIBUTE); | ||||||||||
if (obj instanceof String str) | ||||||||||
return str; | ||||||||||
if (obj instanceof Environment env) | ||||||||||
return env.getName(); | ||||||||||
return null; | ||||||||||
} | ||||||||||
|
||||||||||
/** | ||||||||||
|
@@ -1389,6 +1425,19 @@ public void setMainPath(Path mainPath) | |||||||||
this.mainPath = mainPath; | ||||||||||
} | ||||||||||
|
||||||||||
private Path filterPath(List<Path> paths, String type, Predicate<Path> predicate) | ||||||||||
{ | ||||||||||
List<Path> hits = paths.stream() | ||||||||||
.filter(predicate) | ||||||||||
.toList(); | ||||||||||
if (hits.size() == 1) | ||||||||||
return hits.get(0); | ||||||||||
else if (hits.size() > 1) | ||||||||||
throw new IllegalStateException("More than 1 " + type + " for deployable " + asStringList(hits)); | ||||||||||
|
||||||||||
return null; | ||||||||||
} | ||||||||||
|
||||||||||
private Path calcMainPath() | ||||||||||
{ | ||||||||||
List<Path> livePaths = paths | ||||||||||
|
@@ -1403,37 +1452,52 @@ private Path calcMainPath() | |||||||||
return null; | ||||||||||
|
||||||||||
// XML always win. | ||||||||||
List<Path> xmls = livePaths.stream() | ||||||||||
.filter(FileID::isXml) | ||||||||||
.toList(); | ||||||||||
if (xmls.size() == 1) | ||||||||||
return xmls.get(0); | ||||||||||
else if (xmls.size() > 1) | ||||||||||
throw new IllegalStateException("More than 1 XML for deployable " + asStringList(xmls)); | ||||||||||
Path xml = filterPath(livePaths, "XML", FileID::isXml); | ||||||||||
if (xml != null) | ||||||||||
return xml; | ||||||||||
|
||||||||||
// WAR files are next. | ||||||||||
List<Path> wars = livePaths.stream() | ||||||||||
.filter(FileID::isWebArchive) | ||||||||||
.toList(); | ||||||||||
if (wars.size() == 1) | ||||||||||
return wars.get(0); | ||||||||||
else if (wars.size() > 1) | ||||||||||
throw new IllegalStateException("More than 1 WAR for deployable " + asStringList(wars)); | ||||||||||
Path war = filterPath(livePaths, "WAR", FileID::isWebArchive); | ||||||||||
if (war != null) | ||||||||||
return war; | ||||||||||
|
||||||||||
// JAR files are next. | ||||||||||
Path jar = filterPath(livePaths, "JAR", FileID::isJavaArchive); | ||||||||||
if (jar != null) | ||||||||||
return jar; | ||||||||||
|
||||||||||
// ZIP files are next. | ||||||||||
Path zip = filterPath(livePaths, "ZIP", (p -> FileID.isExtension(p, "zip"))); | ||||||||||
if (zip != null) | ||||||||||
return zip; | ||||||||||
|
||||||||||
// Directories next. | ||||||||||
List<Path> dirs = livePaths.stream() | ||||||||||
.filter(Files::isDirectory) | ||||||||||
.toList(); | ||||||||||
if (dirs.size() == 1) | ||||||||||
return dirs.get(0); | ||||||||||
if (dirs.size() > 1) | ||||||||||
throw new IllegalStateException("More than 1 Directory for deployable " + asStringList(dirs)); | ||||||||||
Path dir = filterPath(livePaths, "Directory", PathsApp::isDeployableDirectory); | ||||||||||
if (dir != null) | ||||||||||
return dir; | ||||||||||
|
||||||||||
// Finally properties files | ||||||||||
Path propertyFile = filterPath(livePaths, "Property File", (p -> FileID.isExtension(p, "properties"))); | ||||||||||
if (propertyFile != null) | ||||||||||
return propertyFile; | ||||||||||
|
||||||||||
LOG.warn("Unable to determine main deployable for {}", this); | ||||||||||
if (LOG.isDebugEnabled()) | ||||||||||
LOG.debug("Unable to determine main deployable for {}", this); | ||||||||||
|
||||||||||
return null; | ||||||||||
} | ||||||||||
|
||||||||||
private static boolean isDeployableDirectory(Path p) | ||||||||||
{ | ||||||||||
if (p == null) | ||||||||||
return false; | ||||||||||
if (Files.isDirectory(p)) | ||||||||||
{ | ||||||||||
return !FileID.isExtension(p, "d"); // ignore nominated dirs | ||||||||||
} | ||||||||||
return false; | ||||||||||
} | ||||||||||
|
||||||||||
public String getName() | ||||||||||
{ | ||||||||||
return name; | ||||||||||
|
@@ -1508,21 +1572,23 @@ public void loadProperties() | |||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
// Look for environment attribute. | ||||||||||
// Verify that environment exists | ||||||||||
Object envObj = getAttributes().getAttribute(ContextHandlerFactory.ENVIRONMENT_ATTRIBUTE); | ||||||||||
if (envObj instanceof Environment environment) | ||||||||||
if (envObj != null) | ||||||||||
{ | ||||||||||
if (LOG.isDebugEnabled()) | ||||||||||
LOG.debug("Using defaulted environment of {} to {}", name, environment); | ||||||||||
} | ||||||||||
else if (envObj instanceof String environmentName) | ||||||||||
{ | ||||||||||
if (StringUtil.isNotBlank(environmentName)) | ||||||||||
if (envObj instanceof String environmentName) | ||||||||||
{ | ||||||||||
if (StringUtil.isNotBlank(environmentName)) | ||||||||||
{ | ||||||||||
Environment env = Environment.get(environmentName); | ||||||||||
if (env == null) | ||||||||||
LOG.warn("Environment not found {}", environmentName); | ||||||||||
} | ||||||||||
} | ||||||||||
else | ||||||||||
{ | ||||||||||
Environment env = Environment.get(environmentName); | ||||||||||
if (LOG.isDebugEnabled()) | ||||||||||
LOG.debug("Setting environment of {} to {}", name, env); | ||||||||||
setEnvironment(env); | ||||||||||
LOG.debug("Unable to use attribute {} as type {}", ContextHandlerFactory.ENVIRONMENT_ATTRIBUTE, envObj.getClass().getName()); | ||||||||||
} | ||||||||||
} | ||||||||||
} | ||||||||||
|
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.
I know we use "ee", but just in case, perhaps this should be