417202 - Start / command line arguments with ${variable} should be expanded

+ Expanding properties & LIB references
This commit is contained in:
Joakim Erdfelt 2013-12-26 12:51:18 -07:00
parent 3d438c2028
commit 4777f4ff3b
18 changed files with 624 additions and 63 deletions

View File

@ -184,14 +184,26 @@ public class BaseHome
if (homePath != null)
{
// logic if home is specified
this.homeDir = homePath;
this.baseDir = basePath == null?homePath:basePath;
this.homeDir = homePath.getAbsoluteFile();
if (basePath == null)
{
this.baseDir = homePath.getAbsoluteFile();
args.getProperties().setProperty("jetty.base",this.baseDir.toString(),"<internal-fallback>");
}
else
{
this.baseDir = basePath.getAbsoluteFile();
}
}
else if (basePath != null)
{
// logic if home is undeclared
this.baseDir = basePath;
this.baseDir = basePath.getAbsoluteFile();
}
// Update System Properties
args.addSystemProperty("jetty.home",this.homeDir.getAbsolutePath());
args.addSystemProperty("jetty.base",this.baseDir.getAbsolutePath());
}
public boolean isBaseDifferent()

View File

@ -519,8 +519,6 @@ public class Main
StartLog.debug("jetty.home=%s",baseHome.getHome());
StartLog.debug("jetty.base=%s",baseHome.getBase());
args.addSystemProperty("jetty.home",baseHome.getHome());
args.addSystemProperty("jetty.base",baseHome.getBase());
// ------------------------------------------------------------
// 3) Load Inis
@ -634,12 +632,12 @@ public class Main
if (args.isStopCommand())
{
int stopPort = Integer.parseInt(args.getProperties().getProperty("STOP.PORT"));
String stopKey = args.getProperties().getProperty("STOP.KEY");
int stopPort = Integer.parseInt(args.getProperties().getString("STOP.PORT"));
String stopKey = args.getProperties().getString("STOP.KEY");
if (args.getProperties().getProperty("STOP.WAIT") != null)
if (args.getProperties().getString("STOP.WAIT") != null)
{
int stopWait = Integer.parseInt(args.getProperties().getProperty("STOP.PORT"));
int stopWait = Integer.parseInt(args.getProperties().getString("STOP.PORT"));
stop(stopPort,stopKey,stopWait);
}

View File

@ -24,7 +24,6 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
/**
* Generate a graphviz dot graph of the modules found
@ -48,7 +47,7 @@ public class ModuleGraphWriter
colorModuleFont = "#888888";
}
public void config(Properties props)
public void config(Props props)
{
String prefix = "jetty.graph.";
colorModuleBg = getProperty(props,prefix + "color.module.bg",colorModuleBg);
@ -59,9 +58,9 @@ public class ModuleGraphWriter
colorModuleFont = getProperty(props,prefix + "color.font",colorModuleFont);
}
private String getProperty(Properties props, String key, String defVal)
private String getProperty(Props props, String key, String defVal)
{
String val = props.getProperty(key,defVal);
String val = props.getString(key,defVal);
if (val == null)
{
return defVal;

View File

@ -0,0 +1,258 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.start;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jetty.start.Props.Prop;
/**
* Management of Properties.
* <p>
* This is larger in scope than the standard {@link java.util.Properties}, as it will also handle tracking the origin of each property, if it was overridden,
* and also allowing for <code>${property}</code> expansion.
*/
public final class Props implements Iterable<Prop>
{
public static class Prop
{
public String key;
public String value;
public String origin;
public Prop overrides;
public Prop(String key, String value, String origin)
{
this.key = key;
this.value = value;
this.origin = origin;
}
public Prop(String key, String value, String origin, Prop overrides)
{
this(key,value,origin);
this.overrides = overrides;
}
}
public static final String ORIGIN_SYSPROP = "<system-property>";
private Map<String, Prop> props = new HashMap<>();
public String cleanReference(String property)
{
String name = property.trim();
if (name.startsWith("${") && name.endsWith("}"))
{
name = name.substring(2,name.length() - 1);
}
return name.trim();
}
public boolean containsKey(String key)
{
return props.containsKey(key);
}
public String expand(String str)
{
return expand(str,new Stack<String>());
}
public String expand(String str, Stack<String> seenStack)
{
if (str == null)
{
return str;
}
if (str.indexOf("${") < 0)
{
// Contains no potential expressions.
return str;
}
if (props.isEmpty())
{
// This is a stupid programming error, we should have something, even system properties
throw new PropsException("Props is empty: no properties declared!?");
}
Pattern pat = Pattern.compile("(?<=[^$]|^)(\\$\\{[^}]*\\})");
Matcher mat = pat.matcher(str);
StringBuilder expanded = new StringBuilder();
int offset = 0;
String property;
String value;
while (mat.find(offset))
{
property = cleanReference(mat.group(1));
// Loop detection
if (seenStack.contains(property))
{
StringBuilder err = new StringBuilder();
err.append("Property expansion loop detected: ");
int idx = seenStack.lastIndexOf(property);
for (int i = idx; i < seenStack.size(); i++)
{
err.append(seenStack.get(i));
err.append(" -> ");
}
err.append(property);
throw new PropsException(err.toString());
}
seenStack.push(property);
// find property name
expanded.append(str.subSequence(offset,mat.start(1)));
// get property value
value = getString(property);
if (value == null)
{
StartLog.debug("Unable to expand: %s",property);
expanded.append(property);
}
else
{
// recursively expand
value = expand(value,seenStack);
expanded.append(value);
}
// update offset
offset = mat.end(1);
}
// leftover
expanded.append(str.substring(offset));
// special case for "$$"
if (expanded.indexOf("$$") >= 0)
{
return expanded.toString().replace("\\$\\$","\\$");
}
return expanded.toString();
}
public Prop getProp(String key)
{
Prop prop = props.get(key);
if (prop == null)
{
// try system property
prop = getSystemProperty(key);
}
return prop;
}
public String getString(String key)
{
if (key == null)
{
throw new PropsException("Cannot get value for null key");
}
String name = cleanReference(key);
if (name.length() == 0)
{
throw new PropsException("Cannot get value for empty key");
}
Prop prop = getProp(name);
if (prop == null)
{
return null;
}
return prop.value;
}
public String getString(String key, String defVal)
{
String val = getString(key);
if (val == null)
{
return defVal;
}
return val;
}
private Prop getSystemProperty(String key)
{
String value = System.getProperty(key);
if (value == null)
{
return null;
}
return new Prop(key,value,ORIGIN_SYSPROP);
}
@Override
public Iterator<Prop> iterator()
{
return props.values().iterator();
}
public void setProperty(Prop prop)
{
props.put(prop.key,prop);
}
public void setProperty(String key, String value, String origin)
{
Prop prop = props.get(key);
if (prop == null)
{
prop = new Prop(key,value,origin);
}
else
{
prop = new Prop(key,value,origin,prop);
}
props.put(key,prop);
}
public int size()
{
return props.size();
}
public void store(OutputStream stream, String comments) throws IOException
{
Properties props = new Properties();
// add all Props as normal properties, with expansion performed.
for (Prop prop : this)
{
props.setProperty(prop.key,expand(prop.value));
}
// write normal properties file
props.store(stream,comments);
}
}

View File

@ -0,0 +1,41 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.start;
/**
* An non-recoverable error with Props usage
*/
@SuppressWarnings("serial")
public class PropsException extends RuntimeException
{
public PropsException(String message, Throwable cause)
{
super(message,cause);
}
public PropsException(String message)
{
super(message);
}
public PropsException(Throwable cause)
{
super(cause);
}
}

View File

@ -18,7 +18,7 @@
package org.eclipse.jetty.start;
import static org.eclipse.jetty.start.UsageException.ERR_BAD_ARG;
import static org.eclipse.jetty.start.UsageException.*;
import java.io.File;
import java.io.FileFilter;
@ -27,20 +27,20 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.eclipse.jetty.start.Props.Prop;
/**
* The Arguments required to start Jetty.
*/
public class StartArgs
{
public static final String CMD_LINE_SOURCE = "<cmd-line>";
public static final String CMD_LINE_SOURCE = "<command-line>";
public static final String VERSION;
static
@ -74,7 +74,7 @@ public class StartArgs
private Classpath classpath;
private List<String> xmlRefs = new ArrayList<>();
private List<File> xmls = new ArrayList<>();
private Properties properties = new Properties();
private Props properties = new Props();
private Set<String> systemPropertyKeys = new HashSet<>();
private List<String> jvmArgs = new ArrayList<>();
private List<String> moduleStartdIni = new ArrayList<>();
@ -170,9 +170,9 @@ public class StartArgs
System.out.println("Jetty Environment:");
System.out.println("-----------------");
dumpSystemProperty("jetty.home");
dumpSystemProperty("jetty.base");
dumpSystemProperty("jetty.version");
dumpProperty("jetty.home");
dumpProperty("jetty.base");
dumpProperty("jetty.version");
}
public void dumpJvmArgs()
@ -206,26 +206,27 @@ public class StartArgs
System.out.println("Properties:");
System.out.println("-----------");
if (properties.isEmpty())
List<String> sortedKeys = new ArrayList<>();
for (Prop prop : properties)
{
if (prop.origin.equals(Props.ORIGIN_SYSPROP))
{
continue; // skip
}
sortedKeys.add(prop.key);
}
if (sortedKeys.isEmpty())
{
System.out.println(" (no properties specified)");
return;
}
List<String> sortedKeys = new ArrayList<>();
@SuppressWarnings("unchecked")
Enumeration<String> keyEnum = (Enumeration<String>)properties.propertyNames();
while (keyEnum.hasMoreElements())
{
sortedKeys.add(keyEnum.nextElement());
}
Collections.sort(sortedKeys);
for (String key : sortedKeys)
{
String value = properties.getProperty(key);
System.out.printf(" %s = %s%n",key,value);
dumpProperty(key);
}
}
@ -248,7 +249,7 @@ public class StartArgs
for (String key : sortedKeys)
{
String value = System.getProperty(key);
System.out.printf(" %s = %s%n",key,value);
System.out.printf(" %s = %s%n",key,properties.expand(value));
}
}
@ -257,6 +258,30 @@ public class StartArgs
System.out.printf(" %s = %s%n",key,System.getProperty(key));
}
private void dumpProperty(String key)
{
Prop prop = properties.getProp(key);
if (prop == null)
{
System.out.printf(" %s (not defined)%n",key);
}
else
{
System.out.printf(" %s = %s%n",key,properties.expand(prop.value));
if (StartLog.isDebugEnabled())
{
System.out.printf(" origin: %s%n",prop.origin);
while (prop.overrides != null)
{
prop = prop.overrides;
System.out.printf(" (overrides)%n");
System.out.printf(" %s = %s%n",key,properties.expand(prop.value));
System.out.printf(" origin: %s%n",prop.origin);
}
}
}
}
/**
* Ensure that the System Properties are set (if defined as a System property, or start.config property, or start.ini property)
*
@ -272,7 +297,7 @@ public class StartArgs
if (properties.containsKey(key))
{
String val = properties.getProperty(key,null);
String val = properties.expand(properties.getString(key));
if (val == null)
{
return; // no value to set
@ -297,7 +322,7 @@ public class StartArgs
// Find and Expand Libraries
for (String rawlibref : module.getLibs())
{
String libref = rawlibref.replace("${jetty.version}",VERSION);
String libref = properties.expand(rawlibref);
libref = FS.separators(libref);
if (libref.contains("*"))
@ -470,7 +495,7 @@ public class StartArgs
return moduleStartIni;
}
public Properties getProperties()
public Props getProperties()
{
return properties;
}
@ -573,6 +598,26 @@ public class StartArgs
return listModules;
}
private void setProperty(String key, String value, String source)
{
// Special / Prevent override from start.ini's
if (key.equals("jetty.home"))
{
properties.setProperty("jetty.home",System.getProperty("jetty.home"),source);
return;
}
// Special / Prevent override from start.ini's
if (key.equals("jetty.base"))
{
properties.setProperty("jetty.base",System.getProperty("jetty.base"),source);
return;
}
// Normal
properties.setProperty(key,value,source);
}
public void setRun(boolean run)
{
this.run = run;
@ -775,9 +820,11 @@ public class StartArgs
{
case 2:
System.setProperty(assign[0],assign[1]);
setProperty(assign[0],assign[1],source);
break;
case 1:
System.setProperty(assign[0],"");
setProperty(assign[0],"",source);
break;
default:
break;
@ -823,7 +870,7 @@ public class StartArgs
StartLog.warn(warn.toString());
}
properties.setProperty(key,value);
setProperty(key,value,source);
return;
}

View File

@ -18,8 +18,7 @@
package org.eclipse.jetty.start;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.*;
import java.io.File;
import java.io.FileNotFoundException;
@ -28,11 +27,11 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jetty.start.Props.Prop;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.junit.Assert;
@ -97,18 +96,15 @@ public class ConfigurationAssert
}
}
List<String> actualProperties = new ArrayList<>();
@SuppressWarnings("unchecked")
Enumeration<String> nameEnum = (Enumeration<String>)args.getProperties().propertyNames();
while (nameEnum.hasMoreElements())
for(Prop prop: args.getProperties())
{
String name = nameEnum.nextElement();
if ("jetty.home".equals(name) || "jetty.base".equals(name))
String name = prop.key;
if ("jetty.home".equals(name) || "jetty.base".equals(name) || prop.origin.equals(Props.ORIGIN_SYSPROP))
{
// strip these out from assertion, to make assertions easier.
continue;
}
String value = args.getProperties().getProperty(name);
actualProperties.add(name + "=" + value);
actualProperties.add(prop.key + "=" + args.getProperties().expand(prop.value));
}
assertContainsUnordered("Properties",expectedProperties,actualProperties);

View File

@ -64,9 +64,9 @@ public class MainTest
System.err.println(args);
//Assert.assertEquals("--stop should not build module tree", 0, args.getEnabledModules().size());
Assert.assertEquals("--stop missing port","10000",args.getProperties().get("STOP.PORT"));
Assert.assertEquals("--stop missing key","foo",args.getProperties().get("STOP.KEY"));
Assert.assertEquals("--stop missing wait","300",args.getProperties().get("STOP.WAIT"));
Assert.assertEquals("--stop missing port","10000",args.getProperties().getString("STOP.PORT"));
Assert.assertEquals("--stop missing key","foo",args.getProperties().getString("STOP.KEY"));
Assert.assertEquals("--stop missing wait","300",args.getProperties().getString("STOP.WAIT"));
}
@Test
@ -76,6 +76,7 @@ public class MainTest
addUseCasesHome(cmdLineArgs);
cmdLineArgs.add("jetty.port=9090");
cmdLineArgs.add("--list-config");
// cmdLineArgs.add("--debug");
Main main = new Main();
StartArgs args = main.processCommandLine(cmdLineArgs.toArray(new String[cmdLineArgs.size()]));

View File

@ -0,0 +1,139 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.start;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.eclipse.jetty.start.Props.Prop;
import org.junit.Test;
public class PropsTest
{
private static final String FROM_TEST = "(test)";
private void assertProp(String prefix, Prop prop, String expectedKey, String expectedValue, String expectedOrigin)
{
assertThat(prefix,prop,notNullValue());
assertThat(prefix + ".key",prop.key,is(expectedKey));
assertThat(prefix + ".value",prop.value,is(expectedValue));
assertThat(prefix + ".origin",prop.origin,is(expectedOrigin));
}
@Test
public void testSystemPropsOnly()
{
Props props = new Props();
String expected = System.getProperty("java.io.tmpdir");
assertThat("System Property",props.getString("java.io.tmpdir"),is(expected));
Prop prop = props.getProp("java.io.tmpdir");
assertProp("System Prop",prop,"java.io.tmpdir",expected,Props.ORIGIN_SYSPROP);
assertThat("System Prop.overrides",prop.overrides,nullValue());
}
@Test
public void testBasic()
{
Props props = new Props();
props.setProperty("name","jetty",FROM_TEST);
String prefix = "Basic";
assertThat(prefix,props.getString("name"),is("jetty"));
Prop prop = props.getProp("name");
assertProp(prefix,prop,"name","jetty",FROM_TEST);
assertThat(prefix + ".overrides",prop.overrides,nullValue());
}
@Test
public void testOverride()
{
Props props = new Props();
props.setProperty("name","jetty",FROM_TEST);
props.setProperty("name","projetty","(Pro-Jetty)");
String prefix = "Overriden";
assertThat(prefix,props.getString("name"),is("projetty"));
Prop prop = props.getProp("name");
assertProp(prefix,prop,"name","projetty","(Pro-Jetty)");
Prop older = prop.overrides;
assertThat(prefix + ".overrides",older,notNullValue());
assertProp(prefix + ".overridden",older,"name","jetty",FROM_TEST);
assertThat(prefix + ".overridden",older.overrides,nullValue());
}
@Test
public void testSimpleExpand()
{
Props props = new Props();
props.setProperty("name","jetty",FROM_TEST);
props.setProperty("version","9.1",FROM_TEST);
assertThat(props.expand("port=8080"),is("port=8080"));
assertThat(props.expand("jdk=${java.version}"),is("jdk=" + System.getProperty("java.version")));
assertThat(props.expand("id=${name}-${version}"),is("id=jetty-9.1"));
}
@Test
public void testNoExpandDoubleDollar()
{
Props props = new Props();
props.setProperty("http.port","8080",FROM_TEST);
// Should NOT expand double $$ symbols
assertThat(props.expand("port=$${http.port}"),is("port=$${http.port}"));
// Should expand
assertThat(props.expand("port=${http.port}"),is("port=8080"));
}
@Test
public void testExpandDeep()
{
Props props = new Props();
props.setProperty("name","jetty",FROM_TEST);
props.setProperty("version","9.1",FROM_TEST);
props.setProperty("id","${name}-${version}",FROM_TEST);
// Should expand
assertThat(props.expand("server-id=corporate-${id}"),is("server-id=corporate-jetty-9.1"));
}
@Test
public void testExpandLoop()
{
Props props = new Props();
props.setProperty("aa","${bb}",FROM_TEST);
props.setProperty("bb","${cc}",FROM_TEST);
props.setProperty("cc","${aa}",FROM_TEST);
try
{
// Should throw exception
props.expand("val=${aa}");
fail("Should have thrown a " + PropsException.class);
}
catch (PropsException e)
{
assertThat(e.getMessage(),is("Property expansion loop detected: aa -> bb -> cc -> aa"));
}
}
}

View File

@ -30,7 +30,7 @@ import org.junit.Test;
*/
public class TestUseCases
{
private void assertUseCase(String homeName, String baseName, String assertName) throws Exception
private void assertUseCase(String homeName, String baseName, String assertName, String... cmdLineArgs) throws Exception
{
File homeDir = MavenTestingUtils.getTestResourceDir("usecases/" + homeName);
File baseDir = MavenTestingUtils.getTestResourceDir("usecases/" + baseName);
@ -39,6 +39,10 @@ public class TestUseCases
List<String> cmdLine = new ArrayList<>();
cmdLine.add("jetty.home=" + homeDir.getAbsolutePath());
cmdLine.add("jetty.base=" + baseDir.getAbsolutePath());
for (String arg : cmdLineArgs)
{
cmdLine.add(arg);
}
StartArgs args = main.processCommandLine(cmdLine);
BaseHome baseHome = main.getBaseHome();
ConfigurationAssert.assertConfiguration(baseHome,args,"usecases/" + assertName);
@ -67,4 +71,16 @@ public class TestUseCases
{
assertUseCase("home","base.with.db","assert-with-db.txt");
}
@Test
public void testWithPropsBasic() throws Exception
{
assertUseCase("home","base.props.basic","assert-props.basic.txt","port=9090");
}
@Test
public void testWithPropsAgent() throws Exception
{
assertUseCase("home","base.props.agent","assert-props.agent.txt","java.vm.specification.version=1.6");
}
}

View File

@ -0,0 +1,20 @@
# The XMLs we expect (order is important)
XML|${jetty.home}/etc/jetty-jmx.xml
XML|${jetty.home}/etc/jetty.xml
XML|${jetty.home}/etc/jetty-http.xml
# The LIBs we expect (order is irrelevant)
LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
LIB|${jetty.home}/lib/jetty-http-TEST.jar
LIB|${jetty.home}/lib/jetty-io-TEST.jar
LIB|${jetty.home}/lib/jetty-jmx-TEST.jar
LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
LIB|${jetty.home}/lib/jetty-server-TEST.jar
LIB|${jetty.home}/lib/jetty-util-TEST.jar
LIB|${jetty.home}/lib/jetty-xml-TEST.jar
LIB|${jetty.home}/lib/servlet-api-3.1.jar
LIB|${jetty.base}/lib/agent-jdk-1.6.jar
# The Properties we expect (order is irrelevant)
PROP|jetty.port=9090
PROP|java.vm.specification.version=1.6

View File

@ -0,0 +1,17 @@
# The XMLs we expect (order is important)
XML|${jetty.home}/etc/jetty.xml
XML|${jetty.home}/etc/jetty-http.xml
# The LIBs we expect (order is irrelevant)
LIB|${jetty.home}/lib/jetty-continuation-TEST.jar
LIB|${jetty.home}/lib/jetty-http-TEST.jar
LIB|${jetty.home}/lib/jetty-io-TEST.jar
LIB|${jetty.home}/lib/jetty-schemas-3.1.jar
LIB|${jetty.home}/lib/jetty-server-TEST.jar
LIB|${jetty.home}/lib/jetty-util-TEST.jar
LIB|${jetty.home}/lib/jetty-xml-TEST.jar
LIB|${jetty.home}/lib/servlet-api-3.1.jar
# The Properties we expect (order is irrelevant)
PROP|jetty.port=9090
PROP|port=9090

View File

@ -0,0 +1,8 @@
[depend]
server
jmx
[lib]
lib/agent-jdk-${java.vm.specification.version}.jar

View File

@ -0,0 +1,4 @@
--module=http,agent
jetty.port=9090

View File

@ -0,0 +1,5 @@
--module=server
--module=http
jetty.port=${port}