diff --git a/Jenkinsfile b/Jenkinsfile index 6f04f1aa31f..4a9352e4c99 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -80,6 +80,31 @@ pipeline { } } } + post { + failure { + slackNotif() + } + unstable { + slackNotif() + } + fixed { + slackNotif() + } + } +} + +def slackNotif() { + script { + if (env.BRANCH_NAME=='jetty-10.0.x' || + env.BRANCH_NAME=='jetty-9.4.x') { + //BUILD_USER = currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserId() + // by ${BUILD_USER} + COLOR_MAP = ['SUCCESS': 'good', 'FAILURE': 'danger', 'UNSTABLE': 'danger', 'ABORTED': 'danger'] + slackSend channel: '#jenkins', + color: COLOR_MAP[currentBuild.currentResult], + message: "*${currentBuild.currentResult}:* Job ${env.JOB_NAME} build ${env.BUILD_NUMBER} - ${env.BUILD_URL}" + } + } } /** diff --git a/jetty-bom/pom.xml b/jetty-bom/pom.xml index e9ac05e1955..e6a585aa457 100644 --- a/jetty-bom/pom.xml +++ b/jetty-bom/pom.xml @@ -211,7 +211,7 @@ org.eclipse.jetty - infinispan-remote + infinispan-common 9.4.19-SNAPSHOT @@ -219,11 +219,6 @@ infinispan-remote-query 9.4.19-SNAPSHOT - - org.eclipse.jetty - infinispan-embedded - 9.4.19-SNAPSHOT - org.eclipse.jetty infinispan-embedded-query diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml index c34cfa218ab..b60d8b9bd26 100644 --- a/jetty-distribution/pom.xml +++ b/jetty-distribution/pom.xml @@ -430,6 +430,27 @@ war true + + org.eclipse.jetty.tests + test-jaas-webapp + ${project.version} + war + true + + + org.eclipse.jetty.tests + test-jndi-webapp + ${project.version} + war + true + + + org.eclipse.jetty.tests + test-spec-webapp + ${project.version} + war + true + org.eclipse.jetty jetty-documentation diff --git a/jetty-jmh/src/main/java/org/eclipse/jetty/util/thread/jmh/ThreadPoolBenchmark.java b/jetty-jmh/src/main/java/org/eclipse/jetty/util/thread/jmh/ThreadPoolBenchmark.java index 0796286afbc..396dec710eb 100644 --- a/jetty-jmh/src/main/java/org/eclipse/jetty/util/thread/jmh/ThreadPoolBenchmark.java +++ b/jetty-jmh/src/main/java/org/eclipse/jetty/util/thread/jmh/ThreadPoolBenchmark.java @@ -18,9 +18,12 @@ package org.eclipse.jetty.util.thread.jmh; +import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import org.eclipse.jetty.util.BlockingArrayQueue; import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.thread.ExecutorThreadPool; import org.eclipse.jetty.util.thread.QueuedThreadPool; @@ -42,16 +45,16 @@ import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; @State(Scope.Benchmark) -@Warmup(iterations = 5, time = 10000, timeUnit = TimeUnit.MILLISECONDS) -@Measurement(iterations = 3, time = 10000, timeUnit = TimeUnit.MILLISECONDS) +@Warmup(iterations = 8, time = 1000, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 3, time = 1000, timeUnit = TimeUnit.MILLISECONDS) public class ThreadPoolBenchmark { public enum Type { - QTP, ETP; + QTP, ETP, LQTP, LETP, AQTP, AETP; } - @Param({ "QTP", "ETP"}) + @Param({ "QTP", "ETP" /*, "LQTP", "LETP", "AQTP", "AETP" */ }) Type type; @Param({ "200" }) @@ -65,11 +68,39 @@ public class ThreadPoolBenchmark switch(type) { case QTP: - pool = new QueuedThreadPool(size,size); + { + QueuedThreadPool qtp = new QueuedThreadPool(size, size, new BlockingArrayQueue<>(32768, 32768)); + qtp.setReservedThreads(0); + pool = qtp; break; + } case ETP: - pool = new ExecutorThreadPool(size,size); + pool = new ExecutorThreadPool(size, size, new BlockingArrayQueue<>(32768, 32768)); + break; + + case LQTP: + { + QueuedThreadPool qtp = new QueuedThreadPool(size, size, new LinkedBlockingQueue<>()); + qtp.setReservedThreads(0); + pool = qtp; + break; + } + + case LETP: + pool = new ExecutorThreadPool(size, size, new LinkedBlockingQueue<>()); + break; + + case AQTP: + { + QueuedThreadPool qtp = new QueuedThreadPool(size, size, new ArrayBlockingQueue<>(32768)); + qtp.setReservedThreads(0); + pool = qtp; + break; + } + + case AETP: + pool = new ExecutorThreadPool(size, size, new ArrayBlockingQueue<>(32768)); break; } LifeCycle.start(pool); diff --git a/jetty-maven-plugin/README_INTEGRATION_TEST.md b/jetty-maven-plugin/README_INTEGRATION_TEST.md index 206b7191097..93ff4c3bd58 100644 --- a/jetty-maven-plugin/README_INTEGRATION_TEST.md +++ b/jetty-maven-plugin/README_INTEGRATION_TEST.md @@ -14,9 +14,11 @@ As they can be long to run, the tests do not run per default. So to run them you Running single test -------------------- -You can run single or set of test as well using the command line argument: ```-Dinvoker.test=jetty-run-mojo-it,jetty-run-war*-it,!jetty-run-distro*``` +You can run single or set of test as well using the command line argument: ```-Dinvoker.test=it-parent-pom,jetty-run-mojo-it,jetty-run-war*-it,!jetty-run-distro*``` The parameter supports pattern and exclusion with ! +Due to [files filtering](http://maven.apache.org/plugins/maven-invoker-plugin/examples/filtering.html), ```it-parent-pom``` must be included - otherwise tests will fail during execution. + Running Logs -------------------- The output of each Maven build will be located in /target/it/${project-name}/build.log diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/common/pom.xml b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/common/pom.xml new file mode 100644 index 00000000000..045791a4491 --- /dev/null +++ b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/common/pom.xml @@ -0,0 +1,12 @@ + + + 4.0.0 + + test.jetty-run-mojo-multi-module-single-war-it + jetty-multi-module-project + 1.0-SNAPSHOT + + common + + diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/common/src/main/java/mca/common/CommonService.java b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/common/src/main/java/mca/common/CommonService.java new file mode 100644 index 00000000000..594d387ae36 --- /dev/null +++ b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/common/src/main/java/mca/common/CommonService.java @@ -0,0 +1,24 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 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 mca.common; + +public class CommonService +{ + +} diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/invoker.properties b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/invoker.properties new file mode 100644 index 00000000000..fd18ebccf10 --- /dev/null +++ b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/invoker.properties @@ -0,0 +1 @@ +invoker.goals = test diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/module/module-api/pom.xml b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/module/module-api/pom.xml new file mode 100644 index 00000000000..33d0edf5d8d --- /dev/null +++ b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/module/module-api/pom.xml @@ -0,0 +1,12 @@ + + + 4.0.0 + + test.jetty-run-mojo-multi-module-single-war-it + module + 1.0-SNAPSHOT + + module-api + + diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/module/module-api/src/main/java/mca/module/ModuleApi.java b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/module/module-api/src/main/java/mca/module/ModuleApi.java new file mode 100644 index 00000000000..c5fc25f27d6 --- /dev/null +++ b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/module/module-api/src/main/java/mca/module/ModuleApi.java @@ -0,0 +1,24 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 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 mca.module; + +public interface ModuleApi +{ + +} diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/module/module-impl/pom.xml b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/module/module-impl/pom.xml new file mode 100644 index 00000000000..8ae3faa0415 --- /dev/null +++ b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/module/module-impl/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + test.jetty-run-mojo-multi-module-single-war-it + module + 1.0-SNAPSHOT + + module-impl + + + + test.jetty-run-mojo-multi-module-single-war-it + module-api + + + diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/module/module-impl/src/main/java/mca/module/ModuleImpl.java b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/module/module-impl/src/main/java/mca/module/ModuleImpl.java new file mode 100644 index 00000000000..ef59d156fcc --- /dev/null +++ b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/module/module-impl/src/main/java/mca/module/ModuleImpl.java @@ -0,0 +1,28 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 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 mca.module; + +import mca.common.CommonService; + +public class ModuleImpl implements ModuleApi +{ + + private static final CommonService cs = new CommonService(); + +} diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/module/pom.xml b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/module/pom.xml new file mode 100644 index 00000000000..80ebae8d327 --- /dev/null +++ b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/module/pom.xml @@ -0,0 +1,25 @@ + + + 4.0.0 + + test.jetty-run-mojo-multi-module-single-war-it + jetty-multi-module-project + 1.0-SNAPSHOT + + module + pom + + + module-api + module-impl + + + + + test.jetty-run-mojo-multi-module-single-war-it + common + + + + diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/pom.xml b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/pom.xml new file mode 100644 index 00000000000..40e1ddbd775 --- /dev/null +++ b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + + org.eclipse.jetty.its + it-parent-pom + 0.0.1-SNAPSHOT + + + test.jetty-run-mojo-multi-module-single-war-it + jetty-multi-module-project + 1.0-SNAPSHOT + pom + + Jetty :: multi-module project + + + common + module + webapp-war + + + + UTF-8 + UTF-8 + 1.8 + @project.version@ + + + + + + test.jetty-run-mojo-multi-module-single-war-it + common + ${project.version} + + + test.jetty-run-mojo-multi-module-single-war-it + module-api + ${project.version} + + + test.jetty-run-mojo-multi-module-single-war-it + module-impl + ${project.version} + + + + + diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/postbuild.groovy b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/postbuild.groovy new file mode 100644 index 00000000000..b8b8b5e2599 --- /dev/null +++ b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/postbuild.groovy @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +File buildLog = new File( basedir, 'build.log' ) +assert buildLog.text.contains( 'Started Jetty Server' ) + +assert buildLog.text.contains( '(1a) >> javax.servlet.ServletContextListener loaded from jar:' ) +assert buildLog.text.contains( 'local-repo/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0.jar!/javax/servlet/ServletContextListener.class << (1b)' ) + +assert buildLog.text.contains( '(2a) >> mca.common.CommonService loaded from file:' ) +assert buildLog.text.contains( 'common/target/classes/mca/common/CommonService.class << (2b)' ) + +assert buildLog.text.contains( '(3a) >> mca.module.ModuleApi loaded from file:' ) +assert buildLog.text.contains( 'module/module-api/target/classes/mca/module/ModuleApi.class << (3b)' ) + +assert buildLog.text.contains( '(4a) >> mca.module.ModuleImpl loaded from file:' ) +assert buildLog.text.contains( 'module/module-impl/target/classes/mca/module/ModuleImpl.class << (4b)' ) + +assert buildLog.text.contains( '(5a) >> mca.webapp.WebAppServletListener loaded from file:' ) +assert buildLog.text.contains( 'webapp-war/target/classes/mca/webapp/WebAppServletListener.class << (5b)' ) diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/webapp-war/pom.xml b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/webapp-war/pom.xml new file mode 100644 index 00000000000..29c3abafafe --- /dev/null +++ b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/webapp-war/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + test.jetty-run-mojo-multi-module-single-war-it + jetty-multi-module-project + 1.0-SNAPSHOT + + webapp-war + war + + + + javax.servlet + javax.servlet-api + + + test.jetty-run-mojo-multi-module-single-war-it + module-impl + + + + + ${project.build.directory}/jetty-run-mojo.txt + + + + + + org.eclipse.jetty + jetty-maven-plugin + + + start-jetty + test-compile + + start + + + + + jetty.port.file + ${jetty.port.file} + + + true + ${basedir}/src/config/jetty.xml + + + + + + + + diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/webapp-war/src/config/jetty.xml b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/webapp-war/src/config/jetty.xml new file mode 100644 index 00000000000..4fb92bbea50 --- /dev/null +++ b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/webapp-war/src/config/jetty.xml @@ -0,0 +1,40 @@ + + + + + + https + + 32768 + 8192 + 8192 + 4096 + + + + + + + + + + + + + + + + + + + + + + + + 0 + 30000 + + + + diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/webapp-war/src/main/java/mca/webapp/WebAppServletListener.java b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/webapp-war/src/main/java/mca/webapp/WebAppServletListener.java new file mode 100644 index 00000000000..af8b389bd18 --- /dev/null +++ b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/webapp-war/src/main/java/mca/webapp/WebAppServletListener.java @@ -0,0 +1,56 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 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 mca.webapp; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import java.net.URL; + +import static java.lang.String.format; + +public class WebAppServletListener implements ServletContextListener +{ + + @Override + public void contextInitialized(ServletContextEvent servletContextEvent) + { + print("1", "javax.servlet.ServletContextListener"); + print("2", "mca.common.CommonService"); + print("3", "mca.module.ModuleApi"); + print("4", "mca.module.ModuleImpl"); + print("5", "mca.webapp.WebAppServletListener"); + } + + @Override + public void contextDestroyed(ServletContextEvent servletContextEvent) + { + + } + + private void print(String counter, String className) + { + String res = className.replaceAll("\\.", "/") + ".class"; + URL url = Thread.currentThread().getContextClassLoader().getResource(res); + System.out.println( + format("(%sa) >> %s loaded from %s << (%sb)", + counter, className, url, counter) + ); + } +} diff --git a/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/webapp-war/src/main/webapp/WEB-INF/web.xml b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/webapp-war/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..5d48dd82060 --- /dev/null +++ b/jetty-maven-plugin/src/it/jetty-run-mojo-multi-module-single-war-it/webapp-war/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,11 @@ + + + + + + + mca.webapp.WebAppServletListener + + diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java index 1bfba008d8c..56b50cf11cf 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java @@ -21,13 +21,15 @@ package org.eclipse.jetty.maven.plugin; import java.io.File; import java.io.IOException; import java.net.URL; -import java.nio.file.Paths; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import org.apache.maven.artifact.Artifact; import org.apache.maven.plugin.MojoExecutionException; @@ -38,10 +40,9 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.util.StringUtils; +import org.eclipse.jetty.maven.plugin.utils.MavenProjectHelper; import org.eclipse.jetty.util.PathWatcher; import org.eclipse.jetty.util.PathWatcher.PathWatchEvent; -import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceCollection; import org.eclipse.jetty.webapp.WebAppContext; @@ -285,8 +286,15 @@ public class JettyRunMojo extends AbstractJettyMojo if (useTestScope && (testClassesDirectory != null)) webApp.setTestClasses (testClassesDirectory); - webApp.setWebInfLib(getDependencyFiles()); - + MavenProjectHelper mavenProjectHelper = new MavenProjectHelper(project); + List webInfLibs = getWebInfLibArtifacts(project).stream() + .map(a -> { + Path p = mavenProjectHelper.getArtifactPath(a); + getLog().debug("Artifact " + a.getId() + " loaded from " + p + " added to WEB-INF/lib"); + return p.toFile(); + }).collect(Collectors.toList()); + getLog().debug("WEB-INF/lib initialized (at root)"); + webApp.setWebInfLib(webInfLibs); //if we have not already set web.xml location, need to set one up if (webApp.getDescriptor() == null) @@ -519,77 +527,41 @@ public class JettyRunMojo extends AbstractJettyMojo startScanner(); getLog().info("Restart completed at "+new Date().toString()); } - - - - - /** - * @return - */ - private List getDependencyFiles() + + private Collection getWebInfLibArtifacts(Set artifacts) { - List dependencyFiles = new ArrayList<>(); - for ( Artifact artifact : projectArtifacts) - { - // Include runtime and compile time libraries, and possibly test libs too - if(artifact.getType().equals("war")) - continue; - - if (Artifact.SCOPE_PROVIDED.equals(artifact.getScope())) - continue; //never add dependencies of scope=provided to the webapp's classpath (see also param) - - if (Artifact.SCOPE_TEST.equals(artifact.getScope()) && !useTestScope) - continue; //only add dependencies of scope=test if explicitly required - - MavenProject mavenProject = getProjectReference( artifact, project ); - - if (mavenProject != null) - { - File projectPath = "test-jar".equals( artifact.getType() )? - Paths.get( mavenProject.getBuild().getTestOutputDirectory() ).toFile() - : Paths.get( mavenProject.getBuild().getOutputDirectory() ).toFile(); - getLog().debug( "Adding project directory " + projectPath.toString() ); - dependencyFiles.add( projectPath ); - continue; - } - - dependencyFiles.add(artifact.getFile()); - getLog().debug( "Adding artifact " + artifact.getFile().getName() + " with scope "+artifact.getScope()+" for WEB-INF/lib " ); - } - - return dependencyFiles; + return artifacts.stream() + .filter(this::canPutArtifactInWebInfLib) + .collect(Collectors.toList()); } - protected MavenProject getProjectReference(Artifact artifact, MavenProject project ) + private Collection getWebInfLibArtifacts(MavenProject mavenProject) { - if ( project.getProjectReferences() == null || project.getProjectReferences().isEmpty() ) + String type = mavenProject.getArtifact().getType(); + if (!"war".equalsIgnoreCase(type) && !"zip".equalsIgnoreCase(type)) { - return null; + return Collections.emptyList(); } - Collection mavenProjects = project.getProjectReferences().values(); - for ( MavenProject mavenProject : mavenProjects ) - { - if ( StringUtils.equals( mavenProject.getId(), artifact.getId() ) ) - { - return mavenProject; - } - if("test-jar".equals(artifact.getType())) - { - // getId use type so comparing getId will fail in case of test-jar dependency - if ( StringUtils.equals( mavenProject.getGroupId(), artifact.getGroupId() ) - && StringUtils.equals( mavenProject.getArtifactId(), artifact.getArtifactId() ) - && StringUtils.equals( mavenProject.getVersion(), artifact.getBaseVersion()) ) - { - return mavenProject; - } - } - } - return null; + return getWebInfLibArtifacts(mavenProject.getArtifacts()); } + private boolean canPutArtifactInWebInfLib(Artifact artifact) + { + if ("war".equalsIgnoreCase(artifact.getType())) + { + return false; + } + if (Artifact.SCOPE_PROVIDED.equals(artifact.getScope())) + { + return false; + } + if (Artifact.SCOPE_TEST.equals(artifact.getScope()) && !useTestScope) + { + return false; + } + return true; + } - - private List getOverlays() throws Exception { diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/utils/MavenProjectHelper.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/utils/MavenProjectHelper.java new file mode 100644 index 00000000000..cdae1dc7a01 --- /dev/null +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/utils/MavenProjectHelper.java @@ -0,0 +1,96 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 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.maven.plugin.utils; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.project.MavenProject; + +public class MavenProjectHelper +{ + + private final Map artifactToLocalProjectMap; + + public MavenProjectHelper(MavenProject project) + { + Set mavenProjects = resolveProjectDependencies(project, new HashSet<>()); + artifactToLocalProjectMap = mavenProjects.stream() + .collect(Collectors.toMap(MavenProject::getId, Function.identity())); + artifactToLocalProjectMap.put(project.getArtifact().getId(), project); + } + + /** + * Gets maven project if referenced in reactor + * @param artifact - maven artifact + * @return {@link MavenProject} if artifact is referenced in reactor, otherwise null + */ + public MavenProject getMavenProject(Artifact artifact) + { + return artifactToLocalProjectMap.get(artifact.getId()); + } + + /** + * Gets path to artifact. + * If artifact is referenced in reactor, returns path to ${project.build.outputDirectory}. + * Otherwise, returns path to location in local m2 repo. + * + * Cannot return null - maven will complain about unsatisfied dependency during project built. + * + * @param artifact maven artifact + * @return path to artifact + */ + public Path getArtifactPath(Artifact artifact) + { + Path path = artifact.getFile().toPath(); + MavenProject mavenProject = getMavenProject(artifact); + if (mavenProject != null) + { + if ( "test-jar".equals( artifact.getType() )) { + path = Paths.get(mavenProject.getBuild().getTestOutputDirectory()); + } else { + path = Paths.get(mavenProject.getBuild().getOutputDirectory()); + } + } + return path; + } + + private static Set resolveProjectDependencies(MavenProject project, Set visitedProjects) + { + if (visitedProjects.contains(project)) + { + return Collections.emptySet(); + } + visitedProjects.add(project); + Set availableProjects = new HashSet<>(project.getProjectReferences().values()); + for (MavenProject ref : project.getProjectReferences().values()) + { + availableProjects.addAll(resolveProjectDependencies(ref, visitedProjects)); + } + return availableProjects; + } + +} diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.java index 755bf42672a..1f023bd432a 100644 --- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.java +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.java @@ -20,7 +20,6 @@ package org.eclipse.jetty.rewrite.handler; import java.io.IOException; import java.util.regex.Matcher; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -32,9 +31,9 @@ import org.eclipse.jetty.util.annotation.Name; *

* The replacement string may use $n" to replace the nth capture group. *

- * All redirects are part of the 3xx Redirection status code set. + * All redirects are part of the {@code 3xx Redirection} status code set. *

- * Defaults to 302 Found + * Defaults to {@code 302 Found} */ public class RedirectRegexRule extends RegexRule { @@ -43,9 +42,9 @@ public class RedirectRegexRule extends RegexRule public RedirectRegexRule() { - this(null,null); + this(null, null); } - + public RedirectRegexRule(@Name("regex") String regex, @Name("location") String location) { super(regex); @@ -54,65 +53,63 @@ public class RedirectRegexRule extends RegexRule setLocation(location); } + /** + * @param replacement the URI to redirect to + * @deprecated use {@link #setLocation(String)} instead. + */ @Deprecated public void setReplacement(String replacement) { - _location = replacement; + setLocation(replacement); } - + + /** + * Sets the redirect location. + * + * @param location the URI to redirect to + */ public void setLocation(String location) { _location = location; } - + /** * Sets the redirect status code. - * + * * @param statusCode the 3xx redirect status code */ public void setStatusCode(int statusCode) { - if ((300 <= statusCode) || (statusCode >= 399)) - { + if (statusCode >= 300 && statusCode <= 399) _statusCode = statusCode; - } else - { throw new IllegalArgumentException("Invalid redirect status code " + statusCode + " (must be a value between 300 and 399)"); - } } - + @Override - protected String apply(String target, HttpServletRequest request, HttpServletResponse response, Matcher matcher) - throws IOException + protected String apply(String target, HttpServletRequest request, HttpServletResponse response, Matcher matcher) throws IOException { - target=_location; - for (int g=1;g<=matcher.groupCount();g++) + target = _location; + for (int g = 1; g <= matcher.groupCount(); g++) { String group = matcher.group(g); - target=target.replaceAll("\\$"+g,group); + target = target.replaceAll("\\$" + g, group); } - + target = response.encodeRedirectURL(target); - response.setHeader("Location",RedirectUtil.toRedirectURL(request,target)); + response.setHeader("Location", RedirectUtil.toRedirectURL(request, target)); response.setStatus(_statusCode); response.getOutputStream().flush(); // no output / content response.getOutputStream().close(); return target; } - + /** * Returns the redirect status code and replacement. */ @Override public String toString() { - StringBuilder str = new StringBuilder(); - str.append(super.toString()); - str.append('[').append(_statusCode); - str.append('>').append(_location); - str.append(']'); - return str.toString(); + return String.format("%s[%d>%s]", super.toString(), _statusCode, _location); } - } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/AtomicBiInteger.java b/jetty-util/src/main/java/org/eclipse/jetty/util/AtomicBiInteger.java index de4e26718e0..e9f9f294a98 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/AtomicBiInteger.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/AtomicBiInteger.java @@ -24,7 +24,22 @@ import java.util.concurrent.atomic.AtomicLong; * An AtomicLong with additional methods to treat it as two hi/lo integers. */ public class AtomicBiInteger extends AtomicLong -{ +{ + + public AtomicBiInteger() + { + } + + public AtomicBiInteger(long encoded) + { + super(encoded); + } + + public AtomicBiInteger(int hi, int lo) + { + super(encode(hi, lo)); + } + /** * @return the hi value */ diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/AtomicTriInteger.java b/jetty-util/src/main/java/org/eclipse/jetty/util/AtomicTriInteger.java deleted file mode 100644 index 6ecec73d382..00000000000 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/AtomicTriInteger.java +++ /dev/null @@ -1,175 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2019 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.util; - -import java.util.concurrent.atomic.AtomicLong; - -/** - * An AtomicLong with additional methods to treat it as three 21 bit unsigned words. - */ -public class AtomicTriInteger extends AtomicLong -{ - public static int MAX_VALUE = 0x1FFFFF; - public static int MIN_VALUE = 0; - - /** - * Sets the hi and lo values. - * - * @param w0 the 0th word - * @param w1 the 1st word - * @param w2 the 2nd word - */ - public void set(int w0, int w1, int w2) - { - set(encode(w0, w1, w2)); - } - - /** - * Atomically sets the word values to the given updated values only if - * the current encoded value is as expected. - * - * @param expectEncoded the expected encoded value - * @param w0 the 0th word - * @param w1 the 1st word - * @param w2 the 2nd word - * @return {@code true} if successful. - */ - public boolean compareAndSet(long expectEncoded, int w0, int w1, int w2) - { - return compareAndSet(expectEncoded, encode(w0, w1, w2)); - } - - /** - * Atomically adds the given deltas to the current hi and lo values. - * - * @param delta0 the delta to apply to the 0th word value - * @param delta1 the delta to apply to the 1st word value - * @param delta2 the delta to apply to the 2nd word value - */ - public void add(int delta0, int delta1, int delta2) - { - while (true) - { - long encoded = get(); - long update = encode( - getWord0(encoded) + delta0, - getWord1(encoded) + delta1, - getWord2(encoded) + delta2); - if (compareAndSet(encoded, update)) - return; - } - } - - /** - * Gets word 0 value - * - * @return the 16 bit value as an int - */ - public int getWord0() - { - return getWord0(get()); - } - - - /** - * Gets word 1 value - * - * @return the 16 bit value as an int - */ - public int getWord1() - { - return getWord1(get()); - } - - /** - * Gets word 2 value - * - * @return the 16 bit value as an int - */ - public int getWord2() - { - return getWord2(get()); - } - - /** - * Gets word 0 value from the given encoded value. - * - * @param encoded the encoded value - * @return the 16 bit value as an int - */ - public static int getWord0(long encoded) - { - return (int)((encoded >> 42) & MAX_VALUE); - } - - /** - * Gets word 0 value from the given encoded value. - * - * @param encoded the encoded value - * @return the 16 bit value as an int - */ - public static int getWord1(long encoded) - { - return (int)((encoded >> 21) & MAX_VALUE); - } - - /** - * Gets word 0 value from the given encoded value. - * - * @param encoded the encoded value - * @return the 16 bit value as an int - */ - public static int getWord2(long encoded) - { - return (int)(encoded & MAX_VALUE); - } - - /** - * Encodes 4 16 bit words values into a long. - * - * @param w0 the 0th word - * @param w1 the 1st word - * @param w2 the 2nd word - * @return the encoded value - */ - public static long encode(int w0, int w1, int w2) - { - if (w0 < MIN_VALUE - || w0 > MAX_VALUE - || w1 < MIN_VALUE - || w1 > MAX_VALUE - || w2 < MIN_VALUE - || w2 > MAX_VALUE) - throw new IllegalArgumentException(String.format("Words must be %d <= word <= %d: %d, %d, %d", MIN_VALUE, MAX_VALUE, w0, w1, w2)); - long wl0 = ((long)w0) & MAX_VALUE; - long wl1 = ((long)w1) & MAX_VALUE; - long wl2 = ((long)w2) & MAX_VALUE; - return (wl0 << 42) + (wl1 << 21) + (wl2); - } - - @Override - public String toString() - { - long encoded = get(); - int w0 = getWord0(encoded); - int w1 = getWord1(encoded); - int w2 = getWord2(encoded); - return String.format("{%d,%d,%d}", w0, w1, w2); - } -} diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/BlockingArrayQueue.java b/jetty-util/src/main/java/org/eclipse/jetty/util/BlockingArrayQueue.java index 4658e88a27f..50596c8fac3 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/BlockingArrayQueue.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/BlockingArrayQueue.java @@ -484,13 +484,56 @@ public class BlockingArrayQueue extends AbstractList implements BlockingQu @Override public int drainTo(Collection c) { - throw new UnsupportedOperationException(); + return drainTo(c, Integer.MAX_VALUE); } @Override public int drainTo(Collection c, int maxElements) { - throw new UnsupportedOperationException(); + int elements = 0; + _tailLock.lock(); + try + { + _headLock.lock(); + try + { + final int head = _indexes[HEAD_OFFSET]; + final int tail = _indexes[TAIL_OFFSET]; + final int capacity = _elements.length; + + int i = head; + while (i!=tail && elements extends AbstractList implements BlockingQu _tailLock.lock(); try { - _headLock.lock(); try { diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutorThreadPool.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutorThreadPool.java index 7b7253c9e29..c9da93cdb64 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutorThreadPool.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ExecutorThreadPool.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -63,7 +64,12 @@ public class ExecutorThreadPool extends ContainerLifeCycle implements ThreadPool public ExecutorThreadPool(int maxThreads, int minThreads) { - this(new ThreadPoolExecutor(maxThreads, maxThreads, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()), minThreads, -1, null); + this(maxThreads, minThreads, new LinkedBlockingQueue<>()); + } + + public ExecutorThreadPool(int maxThreads, int minThreads, BlockingQueue queue) + { + this(new ThreadPoolExecutor(maxThreads, maxThreads, 60, TimeUnit.SECONDS, queue), minThreads, -1, null); } public ExecutorThreadPool(ThreadPoolExecutor executor) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java old mode 100755 new mode 100644 index 7d11e8e19e5..94dab54c6db --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java @@ -29,7 +29,7 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; -import org.eclipse.jetty.util.AtomicTriInteger; +import org.eclipse.jetty.util.AtomicBiInteger; import org.eclipse.jetty.util.BlockingArrayQueue; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; @@ -48,15 +48,16 @@ import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool; public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadPool, Dumpable, TryExecutor { private static final Logger LOG = Log.getLogger(QueuedThreadPool.class); + private static Runnable NOOP = () -> {}; /** - * Encodes thread counts:

- *
Word0
Total thread count (including starting and idle)
- *
Word1
Starting threads
- *
Word2
Idle threads
+ * Encodes thread counts: + *
+ *
Hi
Total thread count or Integer.MIN_VALUE if stopping
+ *
Lo
Net idle threads == idle threads - job queue size
*
*/ - private final AtomicTriInteger _counts = new AtomicTriInteger(); + private final AtomicBiInteger _counts = new AtomicBiInteger(Integer.MIN_VALUE, 0); private final AtomicLong _lastShrink = new AtomicLong(); private final Set _threads = ConcurrentHashMap.newKeySet(); private final Object _joinLock = new Object(); @@ -84,12 +85,17 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP this(maxThreads, Math.min(8, maxThreads)); } - public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads) + public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads) { this(maxThreads, minThreads, 60000); } - public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout")int idleTimeout) + public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("queue") BlockingQueue queue) + { + this(maxThreads, minThreads, 60000, -1, queue, null); + } + + public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout") int idleTimeout) { this(maxThreads, minThreads, idleTimeout, null); } @@ -103,26 +109,24 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP { this(maxThreads, minThreads, idleTimeout, -1, queue, threadGroup); } - + public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout") int idleTimeout, @Name("reservedThreads") int reservedThreads, @Name("queue") BlockingQueue queue, @Name("threadGroup") ThreadGroup threadGroup) { - if (maxThreads < minThreads) { - throw new IllegalArgumentException("max threads ("+maxThreads+") less than min threads (" - +minThreads+")"); - } - + if (maxThreads < minThreads) + throw new IllegalArgumentException("max threads (" + maxThreads + ") less than min threads (" + + minThreads + ")"); setMinThreads(minThreads); setMaxThreads(maxThreads); setIdleTimeout(idleTimeout); setStopTimeout(5000); setReservedThreads(reservedThreads); - if (queue==null) + if (queue == null) { - int capacity=Math.max(_minThreads, 8); - queue=new BlockingArrayQueue<>(capacity, capacity); + int capacity = Math.max(_minThreads, 8) * 1024; + queue = new BlockingArrayQueue<>(capacity, capacity); } - _jobs=queue; - _threadGroup=threadGroup; + _jobs = queue; + _threadGroup = threadGroup; setThreadPoolBudget(new ThreadPoolBudget(this)); } @@ -134,7 +138,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP public void setThreadPoolBudget(ThreadPoolBudget budget) { - if (budget!=null && budget.getSizedThreadPool()!=this) + if (budget != null && budget.getSizedThreadPool() != this) throw new IllegalArgumentException(); _budget = budget; } @@ -142,21 +146,21 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP @Override protected void doStart() throws Exception { - if (_reservedThreads==0) + if (_reservedThreads == 0) { _tryExecutor = NO_TRY; } else { - ReservedThreadExecutor reserved = new ReservedThreadExecutor(this,_reservedThreads); - reserved.setIdleTimeout(_idleTimeout,TimeUnit.MILLISECONDS); + ReservedThreadExecutor reserved = new ReservedThreadExecutor(this, _reservedThreads); + reserved.setIdleTimeout(_idleTimeout, TimeUnit.MILLISECONDS); _tryExecutor = reserved; } addBean(_tryExecutor); - - super.doStart(); - _counts.set(0,0,0); // threads, starting, idle + super.doStart(); + // The threads count set to MIN_VALUE is used to signal to Runners that the pool is stopped. + _counts.set(0, 0); // threads, idle ensureThreads(); } @@ -168,54 +172,59 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP removeBean(_tryExecutor); _tryExecutor = TryExecutor.NO_TRY; - + super.doStop(); + // Signal the Runner threads that we are stopping + int threads = _counts.getAndSetHi(Integer.MIN_VALUE); + + // If stop timeout try to gracefully stop long timeout = getStopTimeout(); BlockingQueue jobs = getQueue(); - - // If no stop timeout, clear job queue - if (timeout <= 0) - jobs.clear(); - - // Fill job Q with noop jobs to wakeup idle - Runnable noop = () -> {}; - for (int i = getThreads(); i-- > 0; ) - jobs.offer(noop); - - // try to let jobs complete naturally for half our stop time - joinThreads(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2); - - // If we still have threads running, get a bit more aggressive - - // interrupt remaining threads - for (Thread thread : _threads) + if (timeout > 0) { - if (LOG.isDebugEnabled()) - LOG.debug("Interrupting {}", thread); - thread.interrupt(); - } - - // wait again for the other half of our stop time - joinThreads(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2); - - Thread.yield(); - if (LOG.isDebugEnabled()) - { - for (Thread unstopped : _threads) + // Fill the job queue with noop jobs to wakeup idle threads. + for (int i = 0; i < threads; ++i) { - StringBuilder dmp = new StringBuilder(); - for (StackTraceElement element : unstopped.getStackTrace()) - { - dmp.append(System.lineSeparator()).append("\tat ").append(element); - } - LOG.warn("Couldn't stop {}{}", unstopped, dmp.toString()); + jobs.offer(NOOP); + } + + // try to let jobs complete naturally for half our stop time + joinThreads(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2); + + // If we still have threads running, get a bit more aggressive + + // interrupt remaining threads + for (Thread thread : _threads) + { + if (LOG.isDebugEnabled()) + LOG.debug("Interrupting {}", thread); + thread.interrupt(); + } + + // wait again for the other half of our stop time + joinThreads(System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2); + + Thread.yield(); + if (LOG.isDebugEnabled()) + { + for (Thread unstopped : _threads) + { + StringBuilder dmp = new StringBuilder(); + for (StackTraceElement element : unstopped.getStackTrace()) + { + dmp.append(System.lineSeparator()).append("\tat ").append(element); + } + LOG.warn("Couldn't stop {}{}", unstopped, dmp.toString()); + } + } + else + { + for (Thread unstopped : _threads) + { + LOG.warn("{} Couldn't stop {}", this, unstopped); + } } - } - else - { - for (Thread unstopped : _threads) - LOG.warn("{} Couldn't stop {}",this,unstopped); } // Close any un-executed jobs @@ -233,11 +242,11 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP LOG.warn(t); } } - else if (job != noop) + else if (job != NOOP) LOG.warn("Stopped without executing or closing {}", job); } - if (_budget!=null) + if (_budget != null) _budget.reset(); synchronized (_joinLock) @@ -259,7 +268,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP } /** - * Thread Pool should use Daemon Threading. + * Thread Pool should use Daemon Threading. * * @param daemon true to enable delegation * @see Thread#setDaemon(boolean) @@ -291,9 +300,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP @Override public void setMaxThreads(int maxThreads) { - if (maxThreadsAtomicTriInteger.MAX_VALUE) - throw new IllegalArgumentException("maxThreads="+maxThreads); - if (_budget!=null) + if (_budget != null) _budget.check(maxThreads); _maxThreads = maxThreads; if (_minThreads > _maxThreads) @@ -317,11 +324,11 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP if (isStarted()) ensureThreads(); } - + /** * Set the number of reserved threads. * - * @param reservedThreads number of reserved threads or -1 for heuristically determined + * @param reservedThreads number of reserved threads or -1 for heuristically determined * @see #getReservedThreads */ public void setReservedThreads(int reservedThreads) @@ -426,10 +433,10 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP { return _priority; } - + /** * Get the size of the job queue. - * + * * @return Number of jobs queued waiting for a thread */ @ManagedAttribute("size of the job queue") @@ -458,7 +465,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP { _detailedDump = detailedDump; } - + @ManagedAttribute("threshold at which the pool is low on threads") public int getLowThreadsThreshold() { @@ -473,22 +480,55 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP @Override public void execute(Runnable job) { - if (!isRunning() || !_jobs.offer(job)) + // Determine if we need to start a thread, use and idle thread or just queue this job + boolean startThread; + while (true) { - LOG.warn("{} rejected {}", this, job); + // Get the atomic counts + long counts = _counts.get(); + + // Get the number of threads started (might not yet be running) + int threads = AtomicBiInteger.getHi(counts); + if (threads == Integer.MIN_VALUE) + throw new RejectedExecutionException(job.toString()); + + // Get the number of truly idle threads. This count is reduced by the + // job queue size so that any threads that are idle but are about to take + // a job from the queue are not counted. + int idle = AtomicBiInteger.getLo(counts); + + // Start a thread if we have insufficient idle threads to meet demand + // and we are not at max threads. + startThread = (idle <= 0 && threads < _maxThreads); + + // The job will be run by an idle thread when available + if (!_counts.compareAndSet(counts, threads + (startThread ? 1 : 0), idle - 1)) + continue; + + break; + } + + if (!_jobs.offer(job)) + { + // reverse our changes to _counts. + if (addCounts(startThread ? -1 : 0, 1)) + LOG.warn("{} rejected {}", this, job); throw new RejectedExecutionException(job.toString()); } + if (LOG.isDebugEnabled()) - LOG.debug("queue {}",job); - // Make sure there is at least one thread executing the job. - ensureThreads(); + LOG.debug("queue {} startThread={}", job, startThread); + + // Start a thread if one was needed + if (startThread) + startThread(); } @Override public boolean tryExecute(Runnable task) { TryExecutor tryExecutor = _tryExecutor; - return tryExecutor!=null && tryExecutor.tryExecute(task); + return tryExecutor != null && tryExecutor.tryExecute(task); } /** @@ -500,11 +540,15 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP synchronized (_joinLock) { while (isRunning()) + { _joinLock.wait(); + } } while (isStopping()) + { Thread.sleep(1); + } } /** @@ -514,7 +558,8 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP @ManagedAttribute("number of threads in the pool") public int getThreads() { - return _counts.getWord0(); + int threads = _counts.getHi(); + return Math.max(0, threads); } /** @@ -524,7 +569,8 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP @ManagedAttribute("number of idle threads in the pool") public int getIdleThreads() { - return _counts.getWord2(); + int idle = _counts.getLo(); + return Math.max(0, idle); } /** @@ -536,7 +582,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP int reserved = _tryExecutor instanceof ReservedThreadExecutor ? ((ReservedThreadExecutor)_tryExecutor).getAvailable() : 0; return getThreads() - getIdleThreads() - reserved; } - + /** *

Returns whether this thread pool is low on threads.

*

The current formula is:

@@ -556,43 +602,63 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP private void ensureThreads() { - while (isRunning()) + while (true) { long counts = _counts.get(); - int threads = AtomicTriInteger.getWord0(counts); - int starting = AtomicTriInteger.getWord1(counts); - int idle = AtomicTriInteger.getWord2(counts); - int queue = getQueueSize(); + int threads = AtomicBiInteger.getHi(counts); + if (threads == Integer.MIN_VALUE) + break; - if (threads >= _maxThreads) - break; - if (threads >= _minThreads && (starting + idle) >= queue) - break; - if (!_counts.compareAndSet(counts, threads + 1, starting + 1, idle)) + // If we have less than min threads + // OR insufficient idle threads to meet demand + int idle = AtomicBiInteger.getLo(counts); + if (threads < _minThreads || (idle < 0 && threads < _maxThreads)) + { + // Then try to start a thread. + if (_counts.compareAndSet(counts, threads + 1, idle)) + startThread(); + // Otherwise continue to check state again. continue; - - boolean started = false; - try - { - if (LOG.isDebugEnabled()) - LOG.debug("Starting thread {}",this); - - Thread thread = newThread(_runnable); - thread.setDaemon(isDaemon()); - thread.setPriority(getThreadsPriority()); - thread.setName(_name + "-" + thread.getId()); - if (LOG.isDebugEnabled()) - LOG.debug("Starting {}", thread); - _threads.add(thread); - _lastShrink.set(System.nanoTime()); - thread.start(); - started = true; - } - finally - { - if (!started) - _counts.add(-1,-1,0); // threads, starting, idle } + break; + } + } + + protected void startThread() + { + boolean started = false; + try + { + Thread thread = newThread(_runnable); + thread.setDaemon(isDaemon()); + thread.setPriority(getThreadsPriority()); + thread.setName(_name + "-" + thread.getId()); + if (LOG.isDebugEnabled()) + LOG.debug("Starting {}", thread); + _threads.add(thread); + _lastShrink.set(System.nanoTime()); + thread.start(); + started = true; + } + finally + { + if (!started) + addCounts(-1, 0); // threads, idle + } + } + + private boolean addCounts(int deltaThreads, int deltaIdle) + { + while (true) + { + long encoded = _counts.get(); + int threads = AtomicBiInteger.getHi(encoded); + int idle = AtomicBiInteger.getLo(encoded); + if (threads == Integer.MIN_VALUE) // This is a marker that the pool is stopped. + return false; + long update = AtomicBiInteger.encode(threads + deltaThreads, idle + deltaIdle); + if (_counts.compareAndSet(encoded, update)) + return true; } } @@ -621,19 +687,19 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP knownMethod = "IDLE "; break; } - + if ("reservedWait".equals(t.getMethodName()) && t.getClassName().endsWith("ReservedThread")) { knownMethod = "RESERVED "; break; } - + if ("select".equals(t.getMethodName()) && t.getClassName().endsWith("SelectorProducer")) { knownMethod = "SELECTING "; break; } - + if ("accept".equals(t.getMethodName()) && t.getClassName().contains("ServerConnector")) { knownMethod = "ACCEPTING "; @@ -664,8 +730,8 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP } else { - int p=thread.getPriority(); - threads.add(thread.getId() + " " + thread.getName() + " " + known + thread.getState() + " @ " + (trace.length > 0 ? trace[0] : "???") + (p==Thread.NORM_PRIORITY?"":(" prio="+p))); + int p = thread.getPriority(); + threads.add(thread.getId() + " " + thread.getName() + " " + known + thread.getState() + " @ " + (trace.length > 0 ? trace[0] : "???") + (p == Thread.NORM_PRIORITY ? "" : (" prio=" + p))); } } @@ -684,12 +750,11 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP public String toString() { long count = _counts.get(); - int threads = AtomicTriInteger.getWord0(count); - int starting = AtomicTriInteger.getWord1(count); - int idle = AtomicTriInteger.getWord2(count); + int threads = Math.max(0, AtomicBiInteger.getHi(count)); + int idle = Math.max(0, AtomicBiInteger.getLo(count)); int queue = getQueueSize(); - return String.format("%s[%s]@%x{%s,%d<=%d<=%d,s=%d,i=%d,r=%d,q=%d}[%s]", + return String.format("%s[%s]@%x{%s,%d<=%d<=%d,i=%d,r=%d,q=%d}[%s]", getClass().getSimpleName(), _name, hashCode(), @@ -697,7 +762,6 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP getMinThreads(), threads, getMaxThreads(), - starting, idle, getReservedThreads(), queue, @@ -768,7 +832,9 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP buf.append(thread.getId()).append(" ").append(thread.getName()).append(" "); buf.append(thread.getState()).append(":").append(System.lineSeparator()); for (StackTraceElement element : thread.getStackTrace()) + { buf.append(" at ").append(element.toString()).append(System.lineSeparator()); + } return buf.toString(); } } @@ -777,102 +843,110 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP private class Runner implements Runnable { - @Override - public void run() - { - boolean idle = false; - Runnable job = null; - - try - { - job = _jobs.poll(); - idle = job==null; - _counts.add(0,-1,idle?1:0); // threads, starting, idle - - if (LOG.isDebugEnabled()) - LOG.debug("Runner started with {} for {}", job, QueuedThreadPool.this); - - while (true) - { - if (job == null) - { - if (!idle) - { - idle = true; - _counts.add(0,0,1); // threads, starting, idle - } - - long idleTimeout = getIdleTimeout(); - job = idleJobPoll(idleTimeout); - - // maybe we should shrink? - if (job == null && getThreads() > _minThreads && idleTimeout > 0) - { - long last = _lastShrink.get(); - long now = System.nanoTime(); - if (last == 0 || (now - last) > TimeUnit.MILLISECONDS.toNanos(idleTimeout)) - { - if (_lastShrink.compareAndSet(last, now)) - { - if (LOG.isDebugEnabled()) - LOG.debug("shrinking {}", QueuedThreadPool.this); - break; - } - } - } - } - - // run job - if (job != null) - { - if (idle) - { - idle = false; - _counts.add(0,0,-1); // threads, starting, idle - } - - if (LOG.isDebugEnabled()) - LOG.debug("run {} in {}", job, QueuedThreadPool.this); - runJob(job); - if (LOG.isDebugEnabled()) - LOG.debug("ran {} in {}", job, QueuedThreadPool.this); - - // Clear interrupted status - Thread.interrupted(); - } - - if (!isRunning()) - break; - - job = _jobs.poll(); - } - } - catch (InterruptedException e) - { - if (LOG.isDebugEnabled()) - LOG.debug("interrupted {} in {}", job, QueuedThreadPool.this); - LOG.ignore(e); - } - catch (Throwable e) - { - LOG.warn(String.format("Unexpected thread death: %s in %s", job, QueuedThreadPool.this), e); - } - finally - { - _counts.add(-1,0,idle?-1:0); // threads, starting, idle - removeThread(Thread.currentThread()); - ensureThreads(); - - if (LOG.isDebugEnabled()) - LOG.debug("Runner exited for {}", QueuedThreadPool.this); - } - } - private Runnable idleJobPoll(long idleTimeout) throws InterruptedException { if (idleTimeout <= 0) return _jobs.take(); return _jobs.poll(idleTimeout, TimeUnit.MILLISECONDS); } + + @Override + public void run() + { + if (LOG.isDebugEnabled()) + LOG.debug("Runner started for {}", QueuedThreadPool.this); + + Runnable job = null; + + try + { + // All threads start idle (not yet taken a job) + if (!addCounts(0, 1)) + return; + + while (true) + { + // If we had a job, signal that we are idle again + if (job != null) + { + if (!addCounts(0, 1)) + break; + } + // else check we are still running + else if (_counts.getHi() == Integer.MIN_VALUE) + { + break; + } + + try + { + // Look for an immediately available job + job = _jobs.poll(); + if (job == null) + { + // Wait for a job + long idleTimeout = getIdleTimeout(); + job = idleJobPoll(idleTimeout); + + // If still no job? + if (job == null) + { + // maybe we should shrink + if (getThreads() > _minThreads && idleTimeout > 0) + { + long last = _lastShrink.get(); + long now = System.nanoTime(); + if (last == 0 || (now - last) > TimeUnit.MILLISECONDS.toNanos(idleTimeout)) + { + if (_lastShrink.compareAndSet(last, now)) + { + if (LOG.isDebugEnabled()) + LOG.debug("shrinking {}", QueuedThreadPool.this); + break; + } + } + } + // continue to try again + continue; + } + } + + // run job + if (LOG.isDebugEnabled()) + LOG.debug("run {} in {}", job, QueuedThreadPool.this); + runJob(job); + if (LOG.isDebugEnabled()) + LOG.debug("ran {} in {}", job, QueuedThreadPool.this); + + // Clear any interrupted status + Thread.interrupted(); + } + catch (InterruptedException e) + { + if (LOG.isDebugEnabled()) + LOG.debug("interrupted {} in {}", job, QueuedThreadPool.this); + LOG.ignore(e); + } + catch (Throwable e) + { + LOG.warn(e); + } + } + } + finally + { + Thread thread = Thread.currentThread(); + removeThread(thread); + + // Decrement the total thread count and the idle count if we had no job + addCounts(-1, job == null ? -1 : 0); + if (LOG.isDebugEnabled()) + LOG.debug("{} exited for {}", thread, QueuedThreadPool.this); + + // There is a chance that we shrunk just as a job was queued for us, so + // check again if we have sufficient threads to meet demand + ensureThreads(); + } + } } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java old mode 100755 new mode 100644 index 0ad6b76bb86..a0c889d162c --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java @@ -21,7 +21,6 @@ package org.eclipse.jetty.util.thread; import java.io.IOException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.util.component.AbstractLifeCycle; @@ -47,13 +46,13 @@ public class ScheduledExecutorScheduler extends AbstractLifeCycle implements Sch public ScheduledExecutorScheduler() { this(null, false); - } + } public ScheduledExecutorScheduler(String name, boolean daemon) { - this (name,daemon, Thread.currentThread().getContextClassLoader()); + this(name, daemon, Thread.currentThread().getContextClassLoader()); } - + public ScheduledExecutorScheduler(String name, boolean daemon, ClassLoader threadFactoryClassLoader) { this(name, daemon, threadFactoryClassLoader, null); @@ -70,16 +69,12 @@ public class ScheduledExecutorScheduler extends AbstractLifeCycle implements Sch @Override protected void doStart() throws Exception { - scheduler = new ScheduledThreadPoolExecutor(1, new ThreadFactory() + scheduler = new ScheduledThreadPoolExecutor(1, r -> { - @Override - public Thread newThread(Runnable r) - { - Thread thread = ScheduledExecutorScheduler.this.thread = new Thread(threadGroup, r, name); - thread.setDaemon(daemon); - thread.setContextClassLoader(classloader); - return thread; - } + Thread thread = ScheduledExecutorScheduler.this.thread = new Thread(threadGroup, r, name); + thread.setDaemon(daemon); + thread.setContextClassLoader(classloader); + return thread; }); scheduler.setRemoveOnCancelPolicy(true); super.doStart(); @@ -97,8 +92,8 @@ public class ScheduledExecutorScheduler extends AbstractLifeCycle implements Sch public Task schedule(Runnable task, long delay, TimeUnit unit) { ScheduledThreadPoolExecutor s = scheduler; - if (s==null) - return ()->false; + if (s == null) + return () -> false; ScheduledFuture result = s.schedule(task, delay, unit); return new ScheduledFutureTask(result); } @@ -116,7 +111,7 @@ public class ScheduledExecutorScheduler extends AbstractLifeCycle implements Sch if (thread == null) Dumpable.dumpObject(out, this); else - Dumpable.dumpObjects(out,indent,this, (Object[])thread.getStackTrace()); + Dumpable.dumpObjects(out, indent, this, (Object[])thread.getStackTrace()); } private static class ScheduledFutureTask implements Task diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/AtomicTriIntegerTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/AtomicTriIntegerTest.java deleted file mode 100644 index cd1bd5ade8c..00000000000 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/AtomicTriIntegerTest.java +++ /dev/null @@ -1,103 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2019 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.util; - -import org.junit.jupiter.api.Test; - -import static org.eclipse.jetty.util.AtomicTriInteger.MAX_VALUE; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class AtomicTriIntegerTest -{ - - @Test - public void testBitOperations() - { - long encoded; - - encoded = AtomicTriInteger.encode(0,0,0); - assertThat(AtomicTriInteger.getWord0(encoded),is(0)); - assertThat(AtomicTriInteger.getWord1(encoded),is(0)); - assertThat(AtomicTriInteger.getWord2(encoded),is(0)); - - encoded = AtomicTriInteger.encode(1,2,3); - assertThat(AtomicTriInteger.getWord0(encoded),is(1)); - assertThat(AtomicTriInteger.getWord1(encoded),is(2)); - assertThat(AtomicTriInteger.getWord2(encoded),is(3)); - - encoded = AtomicTriInteger.encode(MAX_VALUE, MAX_VALUE, MAX_VALUE); - assertThat(AtomicTriInteger.getWord0(encoded),is(MAX_VALUE)); - assertThat(AtomicTriInteger.getWord1(encoded),is(MAX_VALUE)); - assertThat(AtomicTriInteger.getWord2(encoded),is(MAX_VALUE)); - - assertThrows(IllegalArgumentException.class,()-> AtomicTriInteger.encode(-1, MAX_VALUE, MAX_VALUE)); - assertThrows(IllegalArgumentException.class,()-> AtomicTriInteger.encode(MAX_VALUE, -1, MAX_VALUE)); - assertThrows(IllegalArgumentException.class,()-> AtomicTriInteger.encode(MAX_VALUE, MAX_VALUE, -1)); - assertThrows(IllegalArgumentException.class,()-> AtomicTriInteger.encode(MAX_VALUE+1, MAX_VALUE, MAX_VALUE)); - assertThrows(IllegalArgumentException.class,()-> AtomicTriInteger.encode(MAX_VALUE, MAX_VALUE+1, MAX_VALUE)); - assertThrows(IllegalArgumentException.class,()-> AtomicTriInteger.encode(MAX_VALUE, MAX_VALUE, MAX_VALUE+1)); - } - - @Test - public void testSetGet() - { - AtomicTriInteger ati = new AtomicTriInteger(); - ati.set(1,2,3); - assertThat(ati.getWord0(),is(1)); - assertThat(ati.getWord1(),is(2)); - assertThat(ati.getWord2(),is(3)); - } - - @Test - public void testCompareAndSet() - { - AtomicTriInteger ati = new AtomicTriInteger(); - ati.set(1,2,3); - long value = ati.get(); - - ati.set(2,3,4); - assertFalse(ati.compareAndSet(value,5,6,7)); - assertThat(ati.getWord0(),is(2)); - assertThat(ati.getWord1(),is(3)); - assertThat(ati.getWord2(),is(4)); - - value = ati.get(); - assertTrue(ati.compareAndSet(value,6,7,8)); - assertThat(ati.getWord0(),is(6)); - assertThat(ati.getWord1(),is(7)); - assertThat(ati.getWord2(),is(8)); - } - - - @Test - public void testAdd() - { - AtomicTriInteger ati = new AtomicTriInteger(); - ati.set(1,2,3); - ati.add(-1,-2,4); - assertThat(ati.getWord0(),is(0)); - assertThat(ati.getWord1(),is(0)); - assertThat(ati.getWord2(),is(7)); - } - -} diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/BlockingArrayQueueTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/BlockingArrayQueueTest.java index 0d59a1ec4af..3a35ff6fb92 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/BlockingArrayQueueTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/BlockingArrayQueueTest.java @@ -18,12 +18,9 @@ package org.eclipse.jetty.util; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.ListIterator; import java.util.Random; import java.util.concurrent.ConcurrentLinkedQueue; @@ -31,10 +28,16 @@ import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; - +import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledIfSystemProperty; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + public class BlockingArrayQueueTest { @Test @@ -494,4 +497,28 @@ public class BlockingArrayQueueTest assertTrue(iterator.hasNext()); assertFalse(iterator.hasPrevious()); } + + + @Test + public void testDrainTo() throws Exception + { + BlockingArrayQueue queue = new BlockingArrayQueue<>(); + queue.add("one"); + queue.add("two"); + queue.add("three"); + queue.add("four"); + queue.add("five"); + queue.add("six"); + + List to = new ArrayList<>(); + queue.drainTo(to,3); + assertThat(to, Matchers.contains("one", "two", "three")); + assertThat(queue.size(),Matchers.is(3)); + assertThat(queue, Matchers.contains("four", "five", "six")); + + queue.drainTo(to); + assertThat(to, Matchers.contains("one", "two", "three", "four", "five", "six")); + assertThat(queue.size(),Matchers.is(0)); + assertThat(queue, Matchers.empty()); + } } diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java index 1f00ac25f9c..e03322d7b74 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java @@ -181,6 +181,7 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest RunningJob job4=new RunningJob("JOB4"); tp.execute(job4); assertFalse(job4._run.await(1,TimeUnit.SECONDS)); + assertThat(tp.getThreads(),is(4)); // finish job 0 job0._stopping.countDown(); @@ -214,19 +215,21 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest duration = System.nanoTime() - duration; assertThat(TimeUnit.NANOSECONDS.toMillis(duration), Matchers.greaterThan(tp.getIdleTimeout()/2L)); assertThat(TimeUnit.NANOSECONDS.toMillis(duration), Matchers.lessThan(tp.getIdleTimeout()*2L)); + + tp.stop(); } @Test public void testThreadPoolFailingJobs() throws Exception { + QueuedThreadPool tp= new QueuedThreadPool(); + tp.setMinThreads(2); + tp.setMaxThreads(4); + tp.setIdleTimeout(900); + tp.setThreadsPriority(Thread.NORM_PRIORITY-1); + try (StacklessLogging stackless = new StacklessLogging(QueuedThreadPool.class)) { - QueuedThreadPool tp= new QueuedThreadPool(); - tp.setMinThreads(2); - tp.setMaxThreads(4); - tp.setIdleTimeout(900); - tp.setThreadsPriority(Thread.NORM_PRIORITY-1); - tp.start(); // min threads started @@ -297,8 +300,10 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest assertTrue(job4._stopped.await(10,TimeUnit.SECONDS)); waitForIdle(tp,2); - assertThat(tp.getThreads(),is(2)); + waitForThreads(tp,2); } + + tp.stop(); } @Test @@ -340,6 +345,8 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest RunningJob job4 = new RunningJob(); tp.execute(job4); assertTrue(job4._run.await(5, TimeUnit.SECONDS)); + + tp.stop(); } @Test @@ -364,7 +371,7 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest tp.execute(job0); tp.execute(job1); - // Add a more jobs (which should not be run) + // Add more jobs (which should not be run) RunningJob job2=new RunningJob(); CloseableJob job3=new CloseableJob(); RunningJob job4=new RunningJob(); @@ -391,6 +398,8 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest // Verify ClosableJobs have not been run but have been closed assertThat(job4._run.await(200, TimeUnit.MILLISECONDS), is(false)); assertThat(job3._closed.await(200, TimeUnit.MILLISECONDS), is(true)); + + tp.stop(); } @@ -437,6 +446,7 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest } waitForThreads(tp,2); waitForIdle(tp,2); + tp.stop(); } @Test @@ -484,6 +494,27 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest assertEquals(idle, tp.getIdleThreads()); } + + private void waitForReserved(QueuedThreadPool tp, int reserved) + { + long now=TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); + long start=now; + ReservedThreadExecutor reservedThreadExecutor = tp.getBean(ReservedThreadExecutor.class); + while (reservedThreadExecutor.getAvailable()!=reserved && (now-start)<10000) + { + try + { + Thread.sleep(50); + } + catch(InterruptedException ignored) + { + } + now=TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); + } + assertEquals(reserved, reservedThreadExecutor.getAvailable()); + } + + private void waitForThreads(QueuedThreadPool tp, int threads) { long now=TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); @@ -520,6 +551,7 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest Thread.sleep(100); assertThat(tp.getThreads(),greaterThanOrEqualTo(5)); } + tp.stop(); } @Test @@ -548,24 +580,26 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest public void testDump() throws Exception { QueuedThreadPool pool = new QueuedThreadPool(4, 3); + pool.setIdleTimeout(10000); String dump = pool.dump(); // TODO use hamcrest 2.0 regex matcher assertThat(dump,containsString("STOPPED")); - assertThat(dump,containsString(",3<=0<=4,s=0,i=0,r=-1,q=0")); + assertThat(dump,containsString(",3<=0<=4,i=0,r=-1,q=0")); assertThat(dump,containsString("[NO_TRY]")); pool.setReservedThreads(2); dump = pool.dump(); assertThat(dump,containsString("STOPPED")); - assertThat(dump,containsString(",3<=0<=4,s=0,i=0,r=2,q=0")); + assertThat(dump,containsString(",3<=0<=4,i=0,r=2,q=0")); assertThat(dump,containsString("[NO_TRY]")); pool.start(); waitForIdle(pool,3); + Thread.sleep(250); // TODO need to give time for threads to read idle poll after setting idle dump = pool.dump(); assertThat(count(dump," - STARTED"),is(2)); - assertThat(dump,containsString(",3<=3<=4,s=0,i=3,r=2,q=0")); + assertThat(dump,containsString(",3<=3<=4,i=3,r=2,q=0")); assertThat(dump,containsString("[ReservedThreadExecutor@")); assertThat(count(dump," IDLE "),is(3)); assertThat(count(dump," RESERVED "),is(0)); @@ -585,10 +619,10 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest } }); started.await(); - + Thread.sleep(250); // TODO need to give time for threads to read idle poll after setting idle dump = pool.dump(); assertThat(count(dump," - STARTED"),is(2)); - assertThat(dump,containsString(",3<=3<=4,s=0,i=2,r=2,q=0")); + assertThat(dump,containsString(",3<=3<=4,i=2,r=2,q=0")); assertThat(dump,containsString("[ReservedThreadExecutor@")); assertThat(count(dump," IDLE "),is(2)); assertThat(count(dump," WAITING "),is(1)); @@ -598,7 +632,7 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest pool.setDetailedDump(true); dump = pool.dump(); assertThat(count(dump," - STARTED"),is(2)); - assertThat(dump,containsString(",3<=3<=4,s=0,i=2,r=2,q=0")); + assertThat(dump,containsString(",3<=3<=4,i=2,r=2,q=0")); assertThat(dump,containsString("s=0/2")); assertThat(dump,containsString("[ReservedThreadExecutor@")); assertThat(count(dump," IDLE "),is(2)); @@ -607,12 +641,11 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest assertThat(count(dump,"QueuedThreadPoolTest.lambda$testDump$"),is(1)); assertFalse(pool.tryExecute(()->{})); - while(pool.getIdleThreads()==2) - Thread.sleep(10); - + waitForReserved(pool,1); + Thread.sleep(250); // TODO need to give time for threads to read idle poll after setting idle dump = pool.dump(); assertThat(count(dump," - STARTED"),is(2)); - assertThat(dump,containsString(",3<=3<=4,s=0,i=1,r=2,q=0")); + assertThat(dump,containsString(",3<=3<=4,i=1,r=2,q=0")); assertThat(dump,containsString("s=1/2")); assertThat(dump,containsString("[ReservedThreadExecutor@")); assertThat(count(dump," IDLE "),is(1)); diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/FloatDecoderTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/FloatDecoderTest.java new file mode 100644 index 00000000000..0d94a8a35df --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/FloatDecoderTest.java @@ -0,0 +1,68 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 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.websocket.jsr356.decoders; + +import org.junit.jupiter.api.Test; + +import javax.websocket.DecodeException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit tests for class {@link FloatDecoder}. + * + * @see FloatDecoder + */ +public class FloatDecoderTest +{ + + @Test + public void testWillDecodeReturningTrue() + { + assertTrue(new FloatDecoder().willDecode("21")); + } + + @Test + public void testWillDecodeReturningFalse() + { + assertFalse(new FloatDecoder().willDecode("NaN")); + } + + @Test + public void testWillDecodeWithNull() + { + assertFalse(FloatDecoder.INSTANCE.willDecode(null)); + } + + @Test + public void testDecodeThrowsDecodeException() + { + assertThrows(DecodeException.class, () -> new FloatDecoder().decode("NaN")); + } + + @Test + public void testDecode() throws DecodeException + { + assertEquals(4.1F, new FloatDecoder().decode("4.1"), 0.01F); + } + +} diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/IntegerDecoderTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/IntegerDecoderTest.java index 49c1f6afbb4..08f3765b289 100644 --- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/IntegerDecoderTest.java +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/IntegerDecoderTest.java @@ -18,13 +18,14 @@ package org.eclipse.jetty.websocket.jsr356.decoders; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; +import org.junit.jupiter.api.Test; import javax.websocket.DecodeException; - -import org.junit.jupiter.api.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; public class IntegerDecoderTest { @@ -33,6 +34,26 @@ public class IntegerDecoderTest { IntegerDecoder decoder = new IntegerDecoder(); Integer val = decoder.decode("123"); - assertThat("Decoded value",val,is(123)); + assertThat("Decoded value", val, is(123)); } + + @Test + public void testWillDecodeWithNull() + { + assertFalse(new IntegerDecoder().willDecode(null)); + } + + @Test + public void testWillDecodeWithNonEmptyString() + { + assertFalse(new IntegerDecoder().willDecode("a")); + } + + @Test + public void testDecodeThrowsDecodeException() + { + assertThrows(DecodeException.class, () -> IntegerDecoder.INSTANCE.decode("")); + + } + } diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/LongDecoderTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/LongDecoderTest.java new file mode 100644 index 00000000000..e100bf11fca --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/LongDecoderTest.java @@ -0,0 +1,54 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 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.websocket.jsr356.decoders; + +import org.junit.jupiter.api.Test; + +import javax.websocket.DecodeException; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * Unit tests for class {@link LongDecoder}. + * + * @see LongDecoder + */ +public class LongDecoderTest +{ + + @Test + public void testCreatesLongDecoder() + { + assertFalse(new LongDecoder().willDecode(null)); + } + + @Test + public void testWillDecodeWithNonEmptyString() + { + assertFalse(LongDecoder.INSTANCE.willDecode("Unable to parse Long")); + } + + @Test + public void testDecodeThrowsDecodeException() + { + assertThrows(DecodeException.class, () -> LongDecoder.INSTANCE.decode("Unable to parse Long")); + } + +} diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/ShortDecoderTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/ShortDecoderTest.java new file mode 100644 index 00000000000..764323ecafc --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/decoders/ShortDecoderTest.java @@ -0,0 +1,55 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 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.websocket.jsr356.decoders; + +import org.junit.jupiter.api.Test; + +import javax.websocket.DecodeException; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * Unit tests for class {@link ShortDecoder}. + * + * @see ShortDecoder + */ +public class ShortDecoderTest +{ + + @Test + public void testWillDecodeWithNull() + { + assertFalse(new ShortDecoder().willDecode(null)); + } + + @Test + public void testWillDecodeWithNonEmptyString() + { + assertFalse(new ShortDecoder().willDecode(".iix/PN}f[&- ShortDecoder.INSTANCE.decode("$Yta3*m*%")); + + } + +} diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/ReflectUtilsTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/ReflectUtilsTest.java index 83c23a91de6..d9c943a100b 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/ReflectUtilsTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/util/ReflectUtilsTest.java @@ -18,11 +18,13 @@ package org.eclipse.jetty.websocket.common.util; +import org.eclipse.jetty.io.AbstractConnection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Unit tests for class {@link ReflectUtils}. @@ -68,6 +70,18 @@ public class ReflectUtilsTest assertFalse(ReflectUtils.isDefaultConstructable(Integer.class)); } + @Test + public void testIsDefaultConstructableWithAbstractClass() + { + assertFalse(ReflectUtils.isDefaultConstructable(AbstractConnection.class)); + } + + @Test + public void testIsDefaultConstructableWithObjectClass() + { + assertTrue(ReflectUtils.isDefaultConstructable(Object.class)); + } + @Test public void testFindGenericClassForStringClassTwice() { diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java index 09bcefc032d..a2a57075627 100644 --- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java +++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java @@ -37,6 +37,7 @@ import java.security.AccessController; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -68,26 +69,31 @@ import org.xml.sax.SAXException; * {@link ConfigurationProcessorFactory} interface to be found by the * {@link ServiceLoader} by using the DTD and first tag element in the file. * Note that DTD will be null if validation is off.

- *

- * The configuration can be parameterised with properties that are looked up via the - * Property XML element and set on the configuration via the map returned from + *

The configuration can be parameterised with properties that are looked up via the + * Property XML element and set on the configuration via the map returned from * {@link #getProperties()}

- *

- * The configuration can create and lookup beans by ID. If multiple configurations are used, then it - * is good practise to copy the entries from the {@link #getIdMap()} of a configuration to the next + *

The configuration can create and lookup beans by ID. If multiple configurations are used, then it + * is good practise to copy the entries from the {@link #getIdMap()} of a configuration to the next * configuration so that they can share an ID space for beans.

*/ public class XmlConfiguration { private static final Logger LOG = Log.getLogger(XmlConfiguration.class); private static final Class[] __primitives = - {Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE}; + { + Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE + }; private static final Class[] __boxedPrimitives = - {Boolean.class, Character.class, Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Void.class}; + { + Boolean.class, Character.class, Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Void.class + }; private static final Class[] __supportedCollections = - {ArrayList.class, HashSet.class, Queue.class, List.class, Set.class, Collection.class}; + { + ArrayList.class, HashSet.class, Queue.class, List.class, Set.class, Collection.class + }; private static final Iterable __factoryLoader = ServiceLoader.load(ConfigurationProcessorFactory.class); private static final XmlParser __parser = initParser(); + private static XmlParser initParser() { ClassLoader loader = XmlConfiguration.class.getClassLoader(); @@ -96,27 +102,24 @@ public class XmlConfiguration URL config76 = loader.getResource("org/eclipse/jetty/xml/configure_7_6.dtd"); URL config90 = loader.getResource("org/eclipse/jetty/xml/configure_9_0.dtd"); URL config93 = loader.getResource("org/eclipse/jetty/xml/configure_9_3.dtd"); - parser.redirectEntity("configure.dtd",config90); - parser.redirectEntity("configure_1_0.dtd",config60); - parser.redirectEntity("configure_1_1.dtd",config60); - parser.redirectEntity("configure_1_2.dtd",config60); - parser.redirectEntity("configure_1_3.dtd",config60); - parser.redirectEntity("configure_6_0.dtd",config60); - parser.redirectEntity("configure_7_6.dtd",config76); - parser.redirectEntity("configure_9_0.dtd",config90); - parser.redirectEntity("configure_9_3.dtd",config93); - - parser.redirectEntity("http://jetty.mortbay.org/configure.dtd",config93); - parser.redirectEntity("http://jetty.eclipse.org/configure.dtd",config93); - parser.redirectEntity("http://www.eclipse.org/jetty/configure.dtd",config93); - - parser.redirectEntity("-//Mort Bay Consulting//DTD Configure//EN",config93); - parser.redirectEntity("-//Jetty//Configure//EN",config93); - + parser.redirectEntity("configure.dtd", config90); + parser.redirectEntity("configure_1_0.dtd", config60); + parser.redirectEntity("configure_1_1.dtd", config60); + parser.redirectEntity("configure_1_2.dtd", config60); + parser.redirectEntity("configure_1_3.dtd", config60); + parser.redirectEntity("configure_6_0.dtd", config60); + parser.redirectEntity("configure_7_6.dtd", config76); + parser.redirectEntity("configure_9_0.dtd", config90); + parser.redirectEntity("configure_9_3.dtd", config93); + parser.redirectEntity("http://jetty.mortbay.org/configure.dtd", config93); + parser.redirectEntity("http://jetty.eclipse.org/configure.dtd", config93); + parser.redirectEntity("http://www.eclipse.org/jetty/configure.dtd", config93); + parser.redirectEntity("-//Mort Bay Consulting//DTD Configure//EN", config93); + parser.redirectEntity("-//Jetty//Configure//EN", config93); return parser; } - /** + /** * Set the standard IDs and properties expected in a jetty XML file: *
    *
  • RefId Server
  • @@ -127,6 +130,7 @@ public class XmlConfiguration *
  • Property jetty.webapps
  • *
  • Property jetty.webapps.uri
  • *
+ * * @param server The Server object to set * @param webapp The webapps Resource */ @@ -134,18 +138,18 @@ public class XmlConfiguration { try { - if (server!=null) + if (server != null) getIdMap().put("Server", server); Path home = Paths.get(System.getProperty("jetty.home", ".")); - getProperties().put("jetty.home",home.toString()); - getProperties().put("jetty.home.uri",normalizeURI(home.toUri().toASCIIString())); + getProperties().put("jetty.home", home.toString()); + getProperties().put("jetty.home.uri", normalizeURI(home.toUri().toASCIIString())); Path base = Paths.get(System.getProperty("jetty.base", home.toString())); - getProperties().put("jetty.base",base.toString()); - getProperties().put("jetty.base.uri",normalizeURI(base.toUri().toASCIIString())); + getProperties().put("jetty.base", base.toString()); + getProperties().put("jetty.base.uri", normalizeURI(base.toUri().toASCIIString())); - if (webapp!=null) + if (webapp != null) { Path webappPath = webapp.getFile().toPath().toAbsolutePath(); getProperties().put("jetty.webapp", webappPath.toString()); @@ -153,19 +157,19 @@ public class XmlConfiguration getProperties().put("jetty.webapps.uri", normalizeURI(webapp.getURI().toString())); } } - catch(Exception e) + catch (Exception e) { LOG.warn(e); } } - + public static String normalizeURI(String uri) { if (uri.endsWith("/")) - return uri.substring(0,uri.length()-1); + return uri.substring(0, uri.length() - 1); return uri; } - + private final Map _idMap = new HashMap<>(); private final Map _propertyMap = new HashMap<>(); private final URL _url; @@ -183,9 +187,9 @@ public class XmlConfiguration { synchronized (__parser) { - _url=configuration; + _url = configuration; setConfig(__parser.parse(configuration.toString())); - _dtd=__parser.getDTD(); + _dtd = __parser.getDTD(); } } @@ -200,13 +204,13 @@ public class XmlConfiguration public XmlConfiguration(String configuration) throws SAXException, IOException { configuration = "\n" - + configuration; + + configuration; InputSource source = new InputSource(new StringReader(configuration)); synchronized (__parser) { - _url=null; - setConfig( __parser.parse(source)); - _dtd=__parser.getDTD(); + _url = null; + setConfig(__parser.parse(source)); + _dtd = __parser.getDTD(); } } @@ -222,9 +226,9 @@ public class XmlConfiguration InputSource source = new InputSource(configuration); synchronized (__parser) { - _url=null; + _url = null; setConfig(__parser.parse(source)); - _dtd=__parser.getDTD(); + _dtd = __parser.getDTD(); } } @@ -232,40 +236,39 @@ public class XmlConfiguration { if ("Configure".equals(config.getTag())) { - _processor=new JettyXmlConfiguration(); + _processor = new JettyXmlConfiguration(); } - else if (__factoryLoader!=null) + else if (__factoryLoader != null) { for (ConfigurationProcessorFactory factory : __factoryLoader) { _processor = factory.getConfigurationProcessor(_dtd, config.getTag()); - if (_processor!=null) + if (_processor != null) break; } - - if (_processor==null) - throw new IllegalStateException("Unknown configuration type: "+config.getTag()+" in "+this); + if (_processor == null) + throw new IllegalStateException("Unknown configuration type: " + config.getTag() + " in " + this); } else { - throw new IllegalArgumentException("Unknown XML tag:"+config.getTag()); + throw new IllegalArgumentException("Unknown XML tag:" + config.getTag()); } - _processor.init(_url,config,this); + _processor.init(_url, config, this); } - /* ------------------------------------------------------------ */ - /** Get the map of ID String to Objects that is used to hold - * and lookup any objects by ID. + /** + * Get the map of ID String to Objects that is used to hold + * and lookup any objects by ID. *

* A New, Get or Call XML element may have an * id attribute which will cause the resulting object to be placed into * this map. A Ref XML element will lookup an object from this map.

*

- * When chaining configuration files, it is good practise to copy the + * When chaining configuration files, it is good practise to copy the * ID entries from the ID map to the map of the next configuration, so * that they may share an ID space *

- * + * * @return A modifiable map of ID strings to Objects */ public Map getIdMap() @@ -273,10 +276,10 @@ public class XmlConfiguration return _idMap; } - /* ------------------------------------------------------------ */ /** * Get the map of properties used by the Property XML element - * to parameterise configuration. + * to parametrize configuration. + * * @return A modifiable map of properties. */ public Map getProperties() @@ -289,8 +292,8 @@ public class XmlConfiguration * * @param obj The object to be configured, which must be of a type or super type * of the class attribute of the <Configure> element. - * @throws Exception if the configuration fails * @return the configured object + * @throws Exception if the configuration fails */ public Object configure(Object obj) throws Exception { @@ -310,22 +313,21 @@ public class XmlConfiguration { return _processor.configure(); } - - /* ------------------------------------------------------------ */ - /** Initialize a new Object defaults. - *

This method must be called by any {@link ConfigurationProcessor} when it - * creates a new instance of an object before configuring it, so that a derived + + /** + * Initialize a new Object defaults. + *

This method must be called by any {@link ConfigurationProcessor} when it + * creates a new instance of an object before configuring it, so that a derived * XmlConfiguration class may inject default values. + * * @param object the object to initialize defaults on */ public void initializeDefaults(Object object) { } - private static class JettyXmlConfiguration implements ConfigurationProcessor { - private String _url; XmlParser.Node _root; XmlConfiguration _configuration; @@ -333,9 +335,9 @@ public class XmlConfiguration @Override public void init(URL url, XmlParser.Node root, XmlConfiguration configuration) { - _url=url==null?null:url.toString(); - _root=root; - _configuration=configuration; + _url = url == null ? null : url.toString(); + _root = root; + _configuration = configuration; } @Override @@ -345,13 +347,13 @@ public class XmlConfiguration Class oClass = nodeClass(_root); if (oClass != null && !oClass.isInstance(obj)) { - String loaders = (oClass.getClassLoader()==obj.getClass().getClassLoader())?"":"Object Class and type Class are from different loaders."; - throw new IllegalArgumentException("Object of class '"+obj.getClass().getCanonicalName()+"' is not of type '" + oClass.getCanonicalName()+"'. "+loaders+" in "+_url); + String loaders = (oClass.getClassLoader() == obj.getClass().getClassLoader()) ? "" : "Object Class and type Class are from different loaders."; + throw new IllegalArgumentException("Object of class '" + obj.getClass().getCanonicalName() + "' is not of type '" + oClass.getCanonicalName() + "'. " + loaders + " in " + _url); } - String id=_root.getAttribute("id"); - if (id!=null) - _configuration.getIdMap().put(id,obj); - configure(obj,_root,0); + String id = _root.getAttribute("id"); + if (id != null) + _configuration.getIdMap().put(id, obj); + configure(obj, _root, 0); return obj; } @@ -361,7 +363,7 @@ public class XmlConfiguration Class oClass = nodeClass(_root); String id = _root.getAttribute("id"); - Object obj = id == null?null:_configuration.getIdMap().get(id); + Object obj = id == null ? null : _configuration.getIdMap().get(id); int index = 0; if (obj == null && oClass != null) @@ -374,23 +376,21 @@ public class XmlConfiguration { Object o = _root.get(i); if (o instanceof String) - { continue; - } - XmlParser.Node node = (XmlParser.Node)o; - if (!(node.getTag().equals("Arg"))) + XmlParser.Node node = (XmlParser.Node)o; + if (node.getTag().equals("Arg")) { - index = i; - break; + String namedAttribute = node.getAttribute("name"); + Object value = value(null, (XmlParser.Node)o); + if (namedAttribute != null) + namedArgMap.put(namedAttribute, value); + arguments.add(value); } else { - String namedAttribute = node.getAttribute("name"); - Object value=value(obj,(XmlParser.Node)o); - if (namedAttribute != null) - namedArgMap.put(namedAttribute,value); - arguments.add(value); + index = i; + break; } } @@ -403,12 +403,12 @@ public class XmlConfiguration } catch (NoSuchMethodException x) { - throw new IllegalStateException(String.format("No constructor %s(%s,%s) in %s",oClass,arguments,namedArgMap,_url)); + throw new IllegalStateException(String.format("No constructor %s(%s,%s) in %s", oClass, arguments, namedArgMap, _url)); } } - if (id!=null) - _configuration.getIdMap().put(id,obj); - + if (id != null) + _configuration.getIdMap().put(id, obj); + _configuration.initializeDefaults(obj); configure(obj, _root, index); return obj; @@ -419,7 +419,6 @@ public class XmlConfiguration String className = node.getAttribute("class"); if (className == null) return null; - return Loader.loadClass(className); } @@ -443,12 +442,12 @@ public class XmlConfiguration XmlParser.Node node = (XmlParser.Node)o; if ("Arg".equals(node.getTag())) { - LOG.warn("Ignored arg: "+node); + LOG.warn("Ignored arg: " + node); continue; } break; } - + // Process real arguments for (; i < cfg.size(); i++) { @@ -481,10 +480,10 @@ public class XmlConfiguration newArray(obj, node); break; case "Map": - newMap(obj,node); + newMap(obj, node); break; case "Ref": - refObj(obj, node); + refObj(node); break; case "Property": propertyObj(node); @@ -501,26 +500,32 @@ public class XmlConfiguration } catch (Exception e) { - LOG.warn("Config error at " + node,e.toString()+" in "+_url); + LOG.warn("Config error at " + node, e.toString() + " in " + _url); throw e; } } } - /* - * Call a set method. This method makes a best effort to find a matching set method. The type of the value is used to find a suitable set method by 1. - * Trying for a trivial type match. 2. Looking for a native type match. 3. Trying all correctly named methods for an auto conversion. 4. Attempting to - * construct a suitable value from original value. @param obj + /** + *

Call a setter method.

+ *

This method makes a best effort to find a matching set method. + * The type of the value is used to find a suitable set method by:

+ *
    + *
  1. Trying for a trivial type match
  2. + *
  3. Looking for a native type match
  4. + *
  5. Trying all correctly named methods for an auto conversion
  6. + *
  7. Attempting to construct a suitable value from original value
  8. + *
* - * @param node + * @param obj the enclosing object + * @param node the <Set> XML node */ private void set(Object obj, XmlParser.Node node) throws Exception { String attr = node.getAttribute("name"); - String name = "set" + attr.substring(0,1).toUpperCase(Locale.ENGLISH) + attr.substring(1); - Object value = value(obj,node); - Object[] arg = - { value }; + String name = "set" + attr.substring(0, 1).toUpperCase(Locale.ENGLISH) + attr.substring(1); + Object value = value(obj, node); + Object[] arg = {value}; Class oClass = nodeClass(node); if (oClass != null) @@ -528,21 +533,20 @@ public class XmlConfiguration else oClass = obj.getClass(); - Class[] vClass = - { Object.class }; + Class[] vClass = {Object.class}; if (value != null) vClass[0] = value.getClass(); if (LOG.isDebugEnabled()) - LOG.debug("XML " + (obj != null?obj.toString():oClass.getName()) + "." + name + "(" + value + ")"); + LOG.debug("XML " + (obj != null ? obj.toString() : oClass.getName()) + "." + name + "(" + value + ")"); MultiException me = new MultiException(); - + // Try for trivial match try { - Method set = oClass.getMethod(name,vClass); - set.invoke(obj,arg); + Method set = oClass.getMethod(name, vClass); + set.invoke(obj, arg); return; } catch (IllegalArgumentException | IllegalAccessException | NoSuchMethodException e) @@ -556,8 +560,8 @@ public class XmlConfiguration { Field type = vClass[0].getField("TYPE"); vClass[0] = (Class)type.get(null); - Method set = oClass.getMethod(name,vClass); - set.invoke(obj,arg); + Method set = oClass.getMethod(name, vClass); + set.invoke(obj, arg); return; } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException | NoSuchMethodException e) @@ -572,7 +576,7 @@ public class XmlConfiguration Field field = oClass.getField(attr); if (Modifier.isPublic(field.getModifiers())) { - field.set(obj,value); + field.set(obj, value); return; } } @@ -586,19 +590,19 @@ public class XmlConfiguration Method[] sets = oClass.getMethods(); Method set = null; String types = null; - for (int s = 0; sets != null && s < sets.length; s++) + for (Method setter : sets) { - if (sets[s].getParameterCount()!=1) + if (setter.getParameterCount() != 1) continue; - Class[] paramTypes = sets[s].getParameterTypes(); - if (name.equals(sets[s].getName())) + Class[] paramTypes = setter.getParameterTypes(); + if (name.equals(setter.getName())) { - types = types==null?paramTypes[0].getName():(types+","+paramTypes[0].getName()); + types = types == null ? paramTypes[0].getName() : (types + "," + paramTypes[0].getName()); // lets try it try { - set = sets[s]; - sets[s].invoke(obj,arg); + set = setter; + setter.invoke(obj, arg); return; } catch (IllegalArgumentException | IllegalAccessException e) @@ -610,11 +614,13 @@ public class XmlConfiguration try { for (Class c : __supportedCollections) + { if (paramTypes[0].isAssignableFrom(c)) { - sets[s].invoke(obj,convertArrayToCollection(value,c)); + setter.invoke(obj, convertArrayToCollection(value, c)); return; } + } } catch (IllegalAccessException e) { @@ -644,7 +650,7 @@ public class XmlConfiguration Constructor cons = sClass.getConstructor(vClass); arg[0] = cons.newInstance(arg); _configuration.initializeDefaults(arg[0]); - set.invoke(obj,arg); + set.invoke(obj, arg); return; } catch (NoSuchMethodException | IllegalAccessException | InstantiationException e) @@ -656,15 +662,14 @@ public class XmlConfiguration // No Joy String message = oClass + "." + name + "(" + vClass[0] + ")"; - if (types!=null) - message += ". Found setters for "+types; - throw new NoSuchMethodException(message) + if (types != null) + message += ". Found setters for " + types; + NoSuchMethodException failure = new NoSuchMethodException(message); + for (int i = 0; i < me.size(); i++) { - { - for (int i=0; i convertArrayToCollection(Object array, Class collectionType) { + if (array == null) + return null; Collection collection = null; if (array.getClass().isArray()) { @@ -682,7 +689,7 @@ public class XmlConfiguration else if (collectionType.isAssignableFrom(HashSet.class)) collection = new HashSet<>(convertArrayToArrayList(array)); } - if (collection==null) + if (collection == null) throw new IllegalArgumentException("Can't convert \"" + array.getClass() + "\" to " + collectionType); return collection; } @@ -692,14 +699,17 @@ public class XmlConfiguration int length = Array.getLength(array); ArrayList list = new ArrayList<>(length); for (int i = 0; i < length; i++) - list.add(Array.get(array,i)); + { + list.add(Array.get(array, i)); + } return list; } - /* - * Call a put method. + /** + *

Calls a put method.

* - * @param obj @param node + * @param obj the enclosing map object + * @param node the <Put> XML node */ private void put(Object obj, XmlParser.Node node) throws Exception { @@ -710,15 +720,19 @@ public class XmlConfiguration String name = node.getAttribute("name"); Object value = value(obj, node); - map.put(name,value); + map.put(name, value); if (LOG.isDebugEnabled()) LOG.debug("XML " + obj + ".put(" + name + "," + value + ")"); } - /* - * Call a get method. Any object returned from the call is passed to the configure method to consume the remaining elements. @param obj @param node - * If class attribute is given and the name is "class", then the class instance itself is returned. - * @return @exception Exception + /** + *

Calls a getter method.

+ *

Any object returned from the call is passed to the configure method to consume the remaining elements.

+ *

If the "class" attribute is present and its value is "class", then the class instance itself is returned.

+ * + * @param obj the enclosing object + * @param node the <Get> XML node + * @return the result of the getter invocation */ private Object get(Object obj, XmlParser.Node node) throws Exception { @@ -737,24 +751,27 @@ public class XmlConfiguration { // Handle getClass explicitly if ("class".equalsIgnoreCase(name)) - obj=oClass; + { + obj = oClass; + } else { - // try calling a getXxx method. - Method method = oClass.getMethod("get" + name.substring(0,1).toUpperCase(Locale.ENGLISH) + name.substring(1),(java.lang.Class[])null); - obj = method.invoke(obj,(java.lang.Object[])null); + // Try calling a getXxx method. + Method method = oClass.getMethod("get" + name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1)); + obj = method.invoke(obj); } - if (id!=null) - _configuration.getIdMap().put(id,obj); - configure(obj,node,0); + if (id != null) + _configuration.getIdMap().put(id, obj); + configure(obj, node, 0); } catch (NoSuchMethodException nsme) { try { + // Try the field. Field field = oClass.getField(name); obj = field.get(obj); - configure(obj,node,0); + configure(obj, node, 0); } catch (NoSuchFieldException nsfe) { @@ -764,86 +781,86 @@ public class XmlConfiguration return obj; } - /* - * Call a method. A method is selected by trying all methods with matching names and number of arguments. Any object returned from the call is passed to - * the configure method to consume the remaining elements. Note that if this is a static call we consider only methods declared directly in the given - * class. i.e. we ignore any static methods in superclasses. @param obj + /** + *

Calls a method.

+ *

A method is selected by trying all methods with matching names and number of arguments. + * Any object returned from the call is passed to the configure method to consume the remaining elements. + * Note that if this is a static call we consider only methods declared directly in the given class, + * i.e. we ignore any static methods in superclasses. * - * @param node @return @exception Exception + * @param obj the enclosing object + * @param node the <Call> XML node + * @return the result of the method invocation */ private Object call(Object obj, XmlParser.Node node) throws Exception { - AttrOrElementNode aoeNode=new AttrOrElementNode(obj,node,"Id","Name","Class","Arg"); + AttrOrElementNode aoeNode = new AttrOrElementNode(obj, node, "Id", "Name", "Class", "Arg"); String id = aoeNode.getString("Id"); String name = aoeNode.getString("Name"); String clazz = aoeNode.getString("Class"); List args = aoeNode.getList("Arg"); - - + Class oClass; - if (clazz!=null) + if (clazz != null) { // static call - oClass=Loader.loadClass(clazz); - obj=null; + oClass = Loader.loadClass(clazz); + obj = null; } - else if (obj!=null) + else if (obj != null) { oClass = obj.getClass(); } else throw new IllegalArgumentException(node.toString()); - + if (LOG.isDebugEnabled()) LOG.debug("XML call " + name); try { - Object nobj= TypeUtil.call(oClass,name,obj,args.toArray(new Object[args.size()])); + Object nobj = TypeUtil.call(oClass, name, obj, args.toArray(new Object[0])); if (id != null) - _configuration.getIdMap().put(id,nobj); - configure(nobj,node,aoeNode.getNext()); + _configuration.getIdMap().put(id, nobj); + configure(nobj, node, aoeNode.getNext()); return nobj; } catch (NoSuchMethodException e) { - IllegalStateException ise = new IllegalStateException("No Method: " + node + " on " + oClass); - ise.initCause(e); - throw ise; + throw new IllegalStateException("No Method: " + node + " on " + oClass, e); } } - /* - * Create a new value object. + /** + *

Creates a new value object.

* - * @param obj - * @param node - * - * @return @exception Exception + * @param obj the enclosing object + * @param node the <New> XML node + * @return the result of the constructor invocation */ private Object newObj(Object obj, XmlParser.Node node) throws Exception { - AttrOrElementNode aoeNode=new AttrOrElementNode(obj,node,"Id","Class","Arg"); + AttrOrElementNode aoeNode = new AttrOrElementNode(obj, node, "Id", "Class", "Arg"); String id = aoeNode.getString("Id"); String clazz = aoeNode.getString("Class"); List argNodes = aoeNode.getNodes("Arg"); if (LOG.isDebugEnabled()) LOG.debug("XML new " + clazz); - + Class oClass = Loader.loadClass(clazz); - + // Find the elements Map namedArgMap = new HashMap<>(); List arguments = new LinkedList<>(); for (XmlParser.Node child : argNodes) { String namedAttribute = child.getAttribute("name"); - Object value=value(obj,child); + Object value = value(obj, child); if (namedAttribute != null) { // named arguments - namedArgMap.put(namedAttribute,value); + namedArgMap.put(namedAttribute, value); } // raw arguments arguments.add(value); @@ -854,8 +871,8 @@ public class XmlConfiguration { if (namedArgMap.size() > 0) { - LOG.debug("using named mapping"); - nobj = TypeUtil.construct(oClass, arguments.toArray(), namedArgMap); + LOG.debug("using named mapping"); + nobj = TypeUtil.construct(oClass, arguments.toArray(), namedArgMap); } else { @@ -870,39 +887,44 @@ public class XmlConfiguration if (id != null) _configuration.getIdMap().put(id, nobj); - + _configuration.initializeDefaults(nobj); - configure(nobj,node,aoeNode.getNext()); + configure(nobj, node, aoeNode.getNext()); return nobj; } - /* - * Reference an id value object. + /** + *

Returns a reference object mapped to an id.

* - * @param obj @param node @return @exception NoSuchMethodException @exception ClassNotFoundException @exception InvocationTargetException + * @param node the <Ref> XML node + * @return the result of the reference invocation */ - private Object refObj(Object obj, XmlParser.Node node) throws Exception + private Object refObj(XmlParser.Node node) throws Exception { String refid = node.getAttribute("refid"); - if (refid==null) + if (refid == null) refid = node.getAttribute("id"); - obj = _configuration.getIdMap().get(refid); - if (obj == null && node.size()>0) + Object obj = _configuration.getIdMap().get(refid); + if (obj == null && node.size() > 0) throw new IllegalStateException("No object for refid=" + refid); - configure(obj,node,0); + configure(obj, node, 0); return obj; } - /* - * Create a new array object. + /** + *

Creates a new array object.

+ * + * @param obj the enclosing object + * @param node the <Array> XML node + * @return the newly created array */ private Object newArray(Object obj, XmlParser.Node node) throws Exception { - AttrOrElementNode aoeNode=new AttrOrElementNode(obj,node,"Id","Type","Item"); + AttrOrElementNode aoeNode = new AttrOrElementNode(obj, node, "Id", "Type", "Item"); String id = aoeNode.getString("Id"); String type = aoeNode.getString("Type"); List items = aoeNode.getNodes("Item"); - + // Get the type Class aClass = java.lang.Object.class; if (type != null) @@ -927,30 +949,33 @@ public class XmlConfiguration } } } - - Object al = null; + Object al = null; for (XmlParser.Node item : items) { String nid = item.getAttribute("id"); - Object v = value(obj,item); - al = LazyList.add(al,(v == null && aClass.isPrimitive())?0:v); + Object v = value(obj, item); + al = LazyList.add(al, (v == null && aClass.isPrimitive()) ? 0 : v); if (nid != null) - _configuration.getIdMap().put(nid,v); + _configuration.getIdMap().put(nid, v); } - Object array = LazyList.toArray(al,aClass); + Object array = LazyList.toArray(al, aClass); if (id != null) - _configuration.getIdMap().put(id,array); + _configuration.getIdMap().put(id, array); return array; } - /* - * Create a new map object. + /** + *

Creates a new map object.

+ * + * @param obj the enclosing object + * @param node the <Map> XML node + * @return the newly created map */ private Object newMap(Object obj, XmlParser.Node node) throws Exception { - AttrOrElementNode aoeNode=new AttrOrElementNode(node,"Id","Entry"); + AttrOrElementNode aoeNode = new AttrOrElementNode(node, "Id", "Entry"); String id = aoeNode.getString("Id"); List entries = aoeNode.getNodes("Entry"); @@ -984,65 +1009,63 @@ public class XmlConfiguration String kid = key.getAttribute("id"); String vid = value.getAttribute("id"); - Object k = value(obj,key); - Object v = value(obj,value); - map.put(k,v); + Object k = value(obj, key); + Object v = value(obj, value); + map.put(k, v); if (kid != null) - _configuration.getIdMap().put(kid,k); + _configuration.getIdMap().put(kid, k); if (vid != null) - _configuration.getIdMap().put(vid,v); + _configuration.getIdMap().put(vid, v); } return map; } - /* - * Get a Property. + /** + *

Returns the value of a property.

* - * @param node - * @return - * @exception Exception + * @param node the <Property> XML node + * @return the property value */ private Object propertyObj(XmlParser.Node node) throws Exception { - AttrOrElementNode aoeNode=new AttrOrElementNode(node,"Id","Name","Deprecated","Default"); + AttrOrElementNode aoeNode = new AttrOrElementNode(node, "Id", "Name", "Deprecated", "Default"); String id = aoeNode.getString("Id"); - String name = aoeNode.getString("Name",true); + String name = aoeNode.getString("Name", true); List deprecated = aoeNode.getList("Deprecated"); String dftValue = aoeNode.getString("Default"); // Look for a value - Map properties = _configuration.getProperties(); + Map properties = _configuration.getProperties(); String value = properties.get(name); - - // Look for a deprecated name value - String alternate=null; + // Look for a deprecated name value + String alternate = null; if (!deprecated.isEmpty()) { for (Object d : deprecated) - { + { String v = properties.get(StringUtil.valueOf(d)); - if (v!=null) + if (v != null) { - if (value==null) + if (value == null) LOG.warn("Property '{}' is deprecated, use '{}' instead", d, name); else LOG.warn("Property '{}' is deprecated, value from '{}' used", d, name); } - if (alternate==null) - alternate=v;; + if (alternate == null) + alternate = v; } } // use alternate from deprecated - if (value==null) - value=alternate; - + if (value == null) + value = alternate; + // use default value - if (value==null) - value=dftValue; + if (value == null) + value = dftValue; // Set value if ID set if (id != null) @@ -1050,50 +1073,51 @@ public class XmlConfiguration return value; } - /* - * Get a SystemProperty. + /** + *

Returns the value of a system property.

* - * @param node - * @return - * @exception Exception + * @param node the <SystemProperty> XML node + * @return the property value */ private Object systemPropertyObj(XmlParser.Node node) throws Exception { - AttrOrElementNode aoeNode=new AttrOrElementNode(node,"Id","Name","Deprecated","Default"); + AttrOrElementNode aoeNode = new AttrOrElementNode(node, "Id", "Name", "Deprecated", "Default"); String id = aoeNode.getString("Id"); - String name = aoeNode.getString("Name",true); + String name = aoeNode.getString("Name", true); List deprecated = aoeNode.getList("Deprecated"); String dftValue = aoeNode.getString("Default"); // Look for a value String value = System.getProperty(name); - + // Look for a deprecated name value - String alternate=null; + String alternate = null; if (!deprecated.isEmpty()) { for (Object d : deprecated) - { - String v = System.getProperty(StringUtil.valueOf(d)); - if (v!=null) + { + if (d == null) + continue; + String v = System.getProperty(d.toString()); + if (v != null) { - if (value==null) + if (value == null) LOG.warn("SystemProperty '{}' is deprecated, use '{}' instead", d, name); else LOG.warn("SystemProperty '{}' is deprecated, value from '{}' used", d, name); } - if (alternate==null) - alternate=v;; + if (alternate == null) + alternate = v; } } // use alternate from deprecated - if (value==null) - value=alternate; - + if (value == null) + value = alternate; + // use default value - if (value==null) - value=dftValue; + if (value == null) + value = dftValue; // Set value if ID set if (id != null) @@ -1101,42 +1125,41 @@ public class XmlConfiguration return value; } - - /* - * Get a Environment Property. + + /** + *

Returns the value of an environment property.

* - * @param node - * @return - * @exception Exception + * @param node the <Env> XML node + * @return the environment property value */ private Object envObj(XmlParser.Node node) throws Exception { - AttrOrElementNode aoeNode=new AttrOrElementNode(node,"Id","Name","Deprecated","Default"); + AttrOrElementNode aoeNode = new AttrOrElementNode(node, "Id", "Name", "Deprecated", "Default"); String id = aoeNode.getString("Id"); - String name = aoeNode.getString("Name",true); + String name = aoeNode.getString("Name", true); List deprecated = aoeNode.getList("Deprecated"); String dftValue = aoeNode.getString("Default"); // Look for a value String value = System.getenv(name); - + // Look for a deprecated name value - if (value==null && !deprecated.isEmpty()) + if (value == null && !deprecated.isEmpty()) { for (Object d : deprecated) { value = System.getenv(StringUtil.valueOf(d)); - if (value!=null) + if (value != null) { LOG.warn("Property '{}' is deprecated, use '{}' instead", d, name); break; } } } - + // use default value - if (value==null) - value=dftValue; + if (value == null) + value = dftValue; // Set value if ID set if (id != null) @@ -1145,18 +1168,23 @@ public class XmlConfiguration return value; } - /* - * Get the value of an element. If no value type is specified, then white space is trimmed out of the value. If it contains multiple value elements they - * are added as strings before being converted to any specified type. @param node + /** + *

Returns the scalar value of an element

. + *

If no value type is specified, then white space is trimmed out of the value. + * If it contains multiple value elements they are added as strings before being + * converted to any specified type.

+ * + * @param obj the enclosing object + * @param node the XML node + * @return the value of the XML node */ private Object value(Object obj, XmlParser.Node node) throws Exception { - Object value; - // Get the type String type = node.getAttribute("type"); // Try a ref lookup + Object value; String ref = node.getAttribute("ref"); if (ref != null) { @@ -1177,7 +1205,7 @@ public class XmlConfiguration int last = node.size() - 1; // Handle default trim type - if (type == null || !"String".equals(type)) + if (!"String".equals(type)) { // Skip leading white Object item; @@ -1210,8 +1238,10 @@ public class XmlConfiguration } if (first == last) + { // Single Item value - value = itemValue(obj,node.get(first)); + value = itemValue(obj, node.get(first)); + } else { // Get the multiple items as a single string @@ -1219,7 +1249,7 @@ public class XmlConfiguration for (int i = first; i <= last; i++) { Object item = node.get(i); - buf.append(itemValue(obj,item)); + buf.append(itemValue(obj, item)); } value = buf.toString(); } @@ -1241,14 +1271,14 @@ public class XmlConfiguration return value; } - if (isTypeMatchingClass(type,String.class)) + if (isTypeMatchingClass(type, String.class)) return value.toString(); Class pClass = TypeUtil.fromName(type); if (pClass != null) - return TypeUtil.valueOf(pClass,value.toString()); + return TypeUtil.valueOf(pClass, value.toString()); - if (isTypeMatchingClass(type,URL.class)) + if (isTypeMatchingClass(type, URL.class)) { if (value instanceof URL) return value; @@ -1262,7 +1292,7 @@ public class XmlConfiguration } } - if (isTypeMatchingClass(type,InetAddress.class)) + if (isTypeMatchingClass(type, InetAddress.class)) { if (value instanceof InetAddress) return value; @@ -1278,8 +1308,8 @@ public class XmlConfiguration for (Class collectionClass : __supportedCollections) { - if (isTypeMatchingClass(type,collectionClass)) - return convertArrayToCollection(value,collectionClass); + if (isTypeMatchingClass(type, collectionClass)) + return convertArrayToCollection(value, collectionClass); } throw new IllegalStateException("Unknown type " + type); @@ -1290,8 +1320,12 @@ public class XmlConfiguration return classToMatch.getSimpleName().equalsIgnoreCase(type) || classToMatch.getName().equals(type); } - /* - * Get the value of a single element. @param obj @param item @return @exception Exception + /** + *

Returns recursively the value of an element.

+ * + * @param obj the enclosing object + * @param item the initial element value + * @return the recursive value of the element */ private Object itemValue(Object obj, Object item) throws Exception { @@ -1302,17 +1336,17 @@ public class XmlConfiguration XmlParser.Node node = (XmlParser.Node)item; String tag = node.getTag(); if ("Call".equals(tag)) - return call(obj,node); + return call(obj, node); if ("Get".equals(tag)) - return get(obj,node); + return get(obj, node); if ("New".equals(tag)) - return newObj(obj,node); + return newObj(obj, node); if ("Ref".equals(tag)) - return refObj(obj,node); + return refObj(node); if ("Array".equals(tag)) - return newArray(obj,node); + return newArray(obj, node); if ("Map".equals(tag)) - return newMap(obj,node); + return newMap(obj, node); if ("Property".equals(tag)) return propertyObj(node); if ("SystemProperty".equals(tag)) @@ -1320,10 +1354,9 @@ public class XmlConfiguration if ("Env".equals(tag)) return envObj(node); - LOG.warn("Unknown value tag: " + node,new Throwable()); + LOG.warn("Unknown value tag: " + node, new Throwable()); return null; } - private class AttrOrElementNode { @@ -1332,41 +1365,40 @@ public class XmlConfiguration final Set _elements = new HashSet<>(); final int _next; - AttrOrElementNode(XmlParser.Node node,String... elements ) + AttrOrElementNode(XmlParser.Node node, String... elements) { - this(null,node,elements); + this(null, node, elements); } - - AttrOrElementNode(Object obj, XmlParser.Node node,String... elements ) + + AttrOrElementNode(Object obj, XmlParser.Node node, String... elements) { - _obj=obj; - _node=node; - for (String e:elements) - _elements.add(e); - - int next=0; - for (Object o: _node) + _obj = obj; + _node = node; + Collections.addAll(_elements, elements); + + int next = 0; + for (Object o : _node) { if (o instanceof String) { - if (((String)o).trim().length()==0) + if (((String)o).trim().length() == 0) { next++; continue; } break; } - + if (!(o instanceof XmlParser.Node)) break; - + XmlParser.Node n = (XmlParser.Node)o; if (!_elements.contains(n.getTag())) break; - + next++; } - _next=next; + _next = next; } public int getNext() @@ -1376,21 +1408,21 @@ public class XmlConfiguration public String getString(String elementName) throws Exception { - return StringUtil.valueOf(get(elementName,false)); + return StringUtil.valueOf(get(elementName, false)); } - - public String getString(String elementName, boolean manditory) throws Exception + + public String getString(String elementName, boolean mandatory) throws Exception { - return StringUtil.valueOf(get(elementName,manditory)); + return StringUtil.valueOf(get(elementName, mandatory)); } - - public Object get(String elementName, boolean manditory) throws Exception + + public Object get(String elementName, boolean mandatory) throws Exception { - String attrName=StringUtil.asciiToLowerCase(elementName); + String attrName = StringUtil.asciiToLowerCase(elementName); String attr = _node.getAttribute(attrName); - Object value=attr; - - for (int i=0;i<_next;i++) + Object value = attr; + + for (int i = 0; i < _next; i++) { Object o = _node.get(i); if (!(o instanceof XmlParser.Node)) @@ -1398,85 +1430,84 @@ public class XmlConfiguration XmlParser.Node n = (XmlParser.Node)o; if (elementName.equals(n.getTag())) { - if (attr!=null) - throw new IllegalStateException("Cannot have attr '"+attrName+"' and element '"+elementName+"'"); + if (attr != null) + throw new IllegalStateException("Cannot have attr '" + attrName + "' and element '" + elementName + "'"); - value=value(_obj,n); + value = value(_obj, n); break; } } - - if (manditory && value==null) - throw new IllegalStateException("Must have attr '"+attrName+"' or element '"+elementName+"'"); - + + if (mandatory && value == null) + throw new IllegalStateException("Must have attr '" + attrName + "' or element '" + elementName + "'"); + return value; } public List getList(String elementName) throws Exception { - return getList(elementName,false); + return getList(elementName, false); } - + public List getList(String elementName, boolean manditory) throws Exception { - String attrName=StringUtil.asciiToLowerCase(elementName); - final List values=new ArrayList<>(); - + String attrName = StringUtil.asciiToLowerCase(elementName); + final List values = new ArrayList<>(); + String attr = _node.getAttribute(attrName); - if (attr!=null) - values.addAll(StringUtil.csvSplit(null,attr,0,attr.length())); + if (attr != null) + values.addAll(StringUtil.csvSplit(null, attr, 0, attr.length())); - - for (int i=0;i<_next;i++) + for (int i = 0; i < _next; i++) { Object o = _node.get(i); if (!(o instanceof XmlParser.Node)) continue; XmlParser.Node n = (XmlParser.Node)o; - + if (elementName.equals(n.getTag())) { - if (attr!=null) - throw new IllegalStateException("Cannot have attr '"+attrName+"' and element '"+elementName+"'"); + if (attr != null) + throw new IllegalStateException("Cannot have attr '" + attrName + "' and element '" + elementName + "'"); - values.add(value(_obj,n)); + values.add(value(_obj, n)); } } - + if (manditory && values.isEmpty()) - throw new IllegalStateException("Must have attr '"+attrName+"' or element '"+elementName+"'"); + throw new IllegalStateException("Must have attr '" + attrName + "' or element '" + elementName + "'"); return values; } - - public List getNodes(String elementName) throws Exception + + public List getNodes(String elementName) { - String attrName=StringUtil.asciiToLowerCase(elementName); - final List values=new ArrayList<>(); - + String attrName = StringUtil.asciiToLowerCase(elementName); + final List values = new ArrayList<>(); + String attr = _node.getAttribute(attrName); - if (attr!=null) + if (attr != null) { - for (String a : StringUtil.csvSplit(null,attr,0,attr.length())) + for (String a : StringUtil.csvSplit(null, attr, 0, attr.length())) { // create a fake node - XmlParser.Node n = new XmlParser.Node(null,elementName,null); + XmlParser.Node n = new XmlParser.Node(null, elementName, null); n.add(a); values.add(n); } } - for (int i=0;i<_next;i++) + for (int i = 0; i < _next; i++) { Object o = _node.get(i); if (!(o instanceof XmlParser.Node)) continue; XmlParser.Node n = (XmlParser.Node)o; - + if (elementName.equals(n.getTag())) { - if (attr!=null) - throw new IllegalStateException("Cannot have attr '"+attrName+"' and element '"+elementName+"'"); + if (attr != null) + throw new IllegalStateException("Cannot have attr '" + attrName + "' and element '" + elementName + "'"); values.add(n); } @@ -1488,96 +1519,85 @@ public class XmlConfiguration } /** - * Run the XML configurations as a main application. The command line is used to obtain properties files (must be named '*.properties') and XmlConfiguration - * files. + * Runs the XML configurations as a main application. + * The command line is used to obtain properties files (must be named '*.properties') and XmlConfiguration files. *

* Any property file on the command line is added to a combined Property instance that is passed to each configuration file via * {@link XmlConfiguration#getProperties()}. *

- * Each configuration file on the command line is used to create a new XmlConfiguration instance and the {@link XmlConfiguration#configure()} method is used - * to create the configured object. If the resulting object is an instance of {@link LifeCycle}, then it is started. + * Each configuration file on the command line is used to create a new XmlConfiguration instance and the + * {@link XmlConfiguration#configure()} method is used to create the configured object. + * If the resulting object is an instance of {@link LifeCycle}, then it is started. *

* Any IDs created in a configuration are passed to the next configuration file on the command line using {@link #getIdMap()}. * This allows objects with IDs created in one config file to be referenced in subsequent config files on the command line. * - * @param args - * array of property and xml configuration filenames or {@link Resource}s. + * @param args array of property and xml configuration filenames or {@link Resource}s. * @throws Exception if the XML configurations cannot be run */ public static void main(final String... args) throws Exception { try { - AccessController.doPrivileged(new PrivilegedExceptionAction() + AccessController.doPrivileged((PrivilegedExceptionAction)() -> { - @Override - public Void run() throws Exception + Properties properties = new Properties(); + properties.putAll(System.getProperties()); + + // For all arguments, load properties + for (String arg : args) { - Properties properties = null; - - // If no start.config properties, use clean slate - if (properties == null) + if (arg.indexOf('=') >= 0) { - // Add System Properties - properties = new Properties(); - properties.putAll(System.getProperties()); + int i = arg.indexOf('='); + properties.put(arg.substring(0, i), arg.substring(i + 1)); } - - // For all arguments, load properties - for (String arg : args) - { - if (arg.indexOf('=')>=0) - { - int i=arg.indexOf('='); - properties.put(arg.substring(0,i),arg.substring(i+1)); - } - else if (arg.toLowerCase(Locale.ENGLISH).endsWith(".properties")) - properties.load(Resource.newResource(arg).getInputStream()); - } - - // For all arguments, parse XMLs - XmlConfiguration last = null; - List objects = new ArrayList<>(args.length); - for (int i = 0; i < args.length; i++) - { - if (!args[i].toLowerCase(Locale.ENGLISH).endsWith(".properties") && (args[i].indexOf('=')<0)) - { - XmlConfiguration configuration = new XmlConfiguration(Resource.newResource(args[i]).getURI().toURL()); - if (last != null) - configuration.getIdMap().putAll(last.getIdMap()); - if (properties.size() > 0) - { - Map props = new HashMap<>(); - for (Object key : properties.keySet()) - { - props.put(key.toString(),String.valueOf(properties.get(key))); - } - configuration.getProperties().putAll(props); - } - - Object obj = configuration.configure(); - if (obj!=null && !objects.contains(obj)) - objects.add(obj); - last = configuration; - } - } - - // For all objects created by XmlConfigurations, start them if they are lifecycles. - for (Object obj : objects) - { - if (obj instanceof LifeCycle) - { - LifeCycle lc = (LifeCycle)obj; - if (!lc.isRunning()) - lc.start(); - } - } - - return null; + else if (arg.toLowerCase(Locale.ENGLISH).endsWith(".properties")) + properties.load(Resource.newResource(arg).getInputStream()); } + + // For all arguments, parse XMLs + XmlConfiguration last = null; + List objects = new ArrayList<>(args.length); + for (String arg : args) + { + if (!arg.toLowerCase(Locale.ENGLISH).endsWith(".properties") && (arg.indexOf('=') < 0)) + { + XmlConfiguration configuration = new XmlConfiguration(Resource.newResource(arg).getURI().toURL()); + if (last != null) + configuration.getIdMap().putAll(last.getIdMap()); + if (properties.size() > 0) + { + Map props = new HashMap<>(); + for (Object key : properties.keySet()) + { + props.put(key.toString(), String.valueOf(properties.get(key))); + } + configuration.getProperties().putAll(props); + } + + Object obj = configuration.configure(); + if (obj != null && !objects.contains(obj)) + objects.add(obj); + last = configuration; + } + } + + // For all objects created by XmlConfigurations, start them if they are lifecycles. + for (Object obj : objects) + { + if (obj instanceof LifeCycle) + { + LifeCycle lc = (LifeCycle)obj; + if (!lc.isRunning()) + lc.start(); + } + } + + return null; }); - } - catch (Error|Exception e) + } + catch (Error | Exception e) { LOG.warn(e); throw e; diff --git a/pom.xml b/pom.xml index 52f73ed265a..638f85e4ca8 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ undefined 2.1.0 - 7.0 + 7.1 1.21 benchmarks 1.2.0 diff --git a/tests/test-integration/src/test/resources/RFC2616_Redirects.xml b/tests/test-integration/src/test/resources/RFC2616_Redirects.xml index fca59dd34bb..cb380fb060a 100644 --- a/tests/test-integration/src/test/resources/RFC2616_Redirects.xml +++ b/tests/test-integration/src/test/resources/RFC2616_Redirects.xml @@ -8,44 +8,44 @@ - - true - false - requestedPath + + true + false + requestedPath - - + + - - + + - - + + - - - - /redirect/(.*) - /tests/$1 - - - - + + + + /redirect/(.*) + /tests/$1 + + + +