Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NO_MERGE] [JENKINS-54905] - Experimental branch for Java 11 support #41

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions .dockerignore
@@ -0,0 +1,4 @@
Dockerfile
**/target
demo
test
18 changes: 16 additions & 2 deletions Dockerfile
@@ -1,7 +1,7 @@
ARG JENKINS_VERSION=2.121.1
ARG JENKINS_VERSION=jdk11
Copy link
Member

Choose a reason for hiding this comment

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

Do you really intend to modify JFR's master branch with using JDK11 as default? Or maybe we don't care because packagers are always expected to pass this ARG?


# Define maven version for other stages
FROM maven:3.5.2 as maven
FROM maven:3.6.0 as maven

FROM maven as jenkinsfilerunner-mvncache
ADD pom.xml /src/pom.xml
Expand All @@ -23,9 +23,23 @@ RUN cd /jenkinsfile-runner && mvn package

FROM jenkins/jenkins:${JENKINS_VERSION}
USER root

# Libs required to run on Java 11, they come from the base image
ENV JAVA_LIB_DIR /usr/share/jenkins/ref/java_cp

# TODO: java.sql does not help
# 0.555 [id=1] SEVERE jenkins.model.Jenkins#<clinit>: Failed to load Jenkins.class
# java.lang.NoClassDefFoundError: java.sql.Date
# at org.apache.commons.beanutils.ConvertUtilsBean.class$(ConvertUtilsBean.java:157)
ENV JAVA_MODULES "java.xml.bind,java.activation,java.sql"

#TODO: support overrides by the user
ENV JAVA_OPTS "-p ${JAVA_LIB_DIR}/jaxb-api.jar:${JAVA_LIB_DIR}/javax.activation.jar --add-modules ${JAVA_MODULES} -cp ${JAVA_LIB_DIR}/jaxb-impl.jar:${JAVA_LIB_DIR}/jaxb-core.jar"

RUN mkdir /app && unzip /usr/share/jenkins/jenkins.war -d /app/jenkins
COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt

COPY --from=jenkinsfilerunner-build /jenkinsfile-runner/app/target/appassembler /app

ENTRYPOINT ["/app/bin/jenkinsfile-runner", \
Expand Down
1 change: 1 addition & 0 deletions demo/cwp/Makefile
Expand Up @@ -21,4 +21,5 @@ build: .build/cwp-cli-${CWP_VERSION}.jar

run:
docker run --rm -v $(shell pwd)/Jenkinsfile:/workspace/Jenkinsfile \
-e JAVA_OPTS="--add-modules java.sql" \
jenkins-experimental/jenkinsfile-runner-demo
4 changes: 2 additions & 2 deletions demo/cwp/packager-config.yml
Expand Up @@ -14,14 +14,14 @@ buildSettings:
source:
dir: ../..
docker:
base: "jenkins/jenkins:2.138.2"
base: "jenkins/jenkins-experimental:latest-jdk11"
Copy link
Member

Choose a reason for hiding this comment

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

Don't you want to use jenkins/jenkins now we got the official JDK 11 support there, like you did above?

Copy link
Member Author

Choose a reason for hiding this comment

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

agreed

tag: "jenkins-experimental/jenkinsfile-runner-demo"
build: true
war:
groupId: "org.jenkins-ci.main"
artifactId: "jenkins-war"
source:
version: "2.138.2"
version: "2.149"
plugins:
- groupId: "org.jenkins-ci.plugins.workflow"
artifactId: "workflow-job"
Expand Down
1 change: 1 addition & 0 deletions plugins.txt
@@ -1 +1,2 @@
pipeline-model-definition:latest
workflow-support:incrementals;org.jenkins-ci.plugins.workflow;2.21-rc591.43d37d4d080a
Copy link
Member

Choose a reason for hiding this comment

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

Might make sense to bump to 2.23-rc674.73bb98187744 for clarity and consistency with the one we ended up releasing and use on Evergreen: https://github.com/jenkins-infra/evergreen/blob/36a60c268be9529c0f5c019b9ac1b0f192b3032a/services/essentials.yaml#L171

@@ -1,6 +1,8 @@
package io.jenkins.jenkinsfile.runner;

import com.sun.org.apache.bcel.internal.util.ClassLoader;
import hudson.security.ACL;
import io.jenkins.jenkinsfile.runner.util.JoinClassLoader;
import jenkins.slaves.DeprecatedAgentProtocolMonitor;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.security.LoginService;
Expand All @@ -14,6 +16,7 @@

import javax.servlet.ServletContext;
import java.io.File;
import java.net.URLClassLoader;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
Expand All @@ -37,6 +40,11 @@ public JenkinsfileRunnerRule(File warDir, File pluginsDir) {
this.pluginsDir = pluginsDir;
}

private static boolean isPostJava8() {
String javaVersion = System.getProperty("java.version");
return !javaVersion.startsWith("1.");
}

/**
* Sets up Jetty without any actual TCP port serving HTTP.
*/
Expand All @@ -46,7 +54,16 @@ protected ServletContext createWebServer() throws Exception {
server = new Server(queuedThreadPool);

WebAppContext context = new WebAppContext(warDir.getPath(), contextPath);
context.setClassLoader(getClass().getClassLoader());
String javaVersion = System.getProperty("java.version");
Copy link
Member

Choose a reason for hiding this comment

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

unused, I guess leftover from the new isPostJava8() method introduced above.


// if (javaVersion.startsWith("1.")) {
Copy link
Member

Choose a reason for hiding this comment

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

Let's remove the commented out code?

Also, if you mean to keep it, then I guess you'd use isPostJava8()

// context.setClassLoader(getClass().getClassLoader());
// } else {
ClassLoader platform = (ClassLoader) ClassLoader.class.getMethod("getPlatformClassLoader").invoke(null);
Copy link
Member

@batmat batmat Nov 30, 2018

Choose a reason for hiding this comment

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

Can we have a comment explaining the intent a bit here?

Copy link

Choose a reason for hiding this comment

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

Yes please!

Copy link
Contributor

Choose a reason for hiding this comment

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

IIUC, the goal here is get exactly the same classloader as in previous JDK versions. Since this piece of code is executed always and not only with java11, what will happen if this code is executed with java8? The getPlatformClassLoader is only available from java9 (if I'm not wrong), so it will throw an exception

java.lang.ClassLoader cl = new JoinClassLoader(getClass().getClassLoader(), platform);
context.setClassLoader(cl);
// }

context.setConfigurations(new Configuration[]{new WebXmlConfiguration()});
context.addBean(new NoListenerConfiguration(context));
server.setHandler(context);
Expand Down
@@ -0,0 +1,90 @@
package io.jenkins.jenkinsfile.runner.util;// Copyright 2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
// www.source-code.biz, www.inventec.ch/chdh
//
// This module is multi-licensed and may be used under the terms
// of any of the following licenses:
//
// LGPL, GNU Lesser General Public License, V2 or later, http://www.gnu.org/licenses/lgpl.html
Copy link
Member Author

Choose a reason for hiding this comment

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

Note that this code is not compatible w.r.t the license. I put it here only for the experiment (which does not work so far), and it should not be merged as is

// AL, Apache License, V2.0 or later, http://www.apache.org/licenses
//
// Please contact the author if you need another license.
// This module is provided "as is", without warranties of any kind.

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.ByteBuffer;
import java.security.SecureClassLoader;
import java.util.Enumeration;
import java.util.Vector;

/**
* A class loader that combines multiple class loaders into one.<br>
* The classes loaded by this class loader are associated with this class loader,
* i.e. Class.getClassLoader() points to this class loader.
*/
public class JoinClassLoader extends ClassLoader {

private ClassLoader[] delegateClassLoaders;

public JoinClassLoader (ClassLoader parent, ClassLoader... delegateClassLoaders) {
super(parent);
this.delegateClassLoaders = delegateClassLoaders; }

protected Class<?> findClass (String name) throws ClassNotFoundException {
// It would be easier to call the loadClass() methods of the delegateClassLoaders
// here, but we have to load the class from the byte code ourselves, because we
// need it to be associated with our class loader.
String path = name.replace('.', '/') + ".class";
URL url = findResource(path);
if (url == null) {
throw new ClassNotFoundException(name); }
ByteBuffer byteCode;
try {
byteCode = loadResource(url); }
catch (IOException e) {
throw new ClassNotFoundException(name, e); }
return defineClass(name, byteCode, null); }

private ByteBuffer loadResource (URL url) throws IOException {
InputStream stream = null;
try {
stream = url.openStream();
int initialBufferCapacity = Math.min(0x40000, stream.available() + 1);
if (initialBufferCapacity <= 2) {
initialBufferCapacity = 0x10000; }
else {
initialBufferCapacity = Math.max(initialBufferCapacity, 0x200); }
ByteBuffer buf = ByteBuffer.allocate(initialBufferCapacity);
while (true) {
if (!buf.hasRemaining()) {
ByteBuffer newBuf = ByteBuffer.allocate(2*buf.capacity());
buf.flip();
newBuf.put(buf);
buf = newBuf; }
int len = stream.read(buf.array(), buf.position(), buf.remaining());
if (len <= 0) {
break; }
buf.position(buf.position()+len); }
buf.flip();
return buf; }
finally {
if (stream != null) {
stream.close(); }}}

protected URL findResource (String name) {
for (ClassLoader delegate : delegateClassLoaders) {
URL resource = delegate.getResource(name);
if (resource != null) {
return resource; }}
return null; }

protected Enumeration<URL> findResources (String name) throws IOException {
Vector<URL> vector = new Vector<URL>();
for (ClassLoader delegate : delegateClassLoaders) {
Enumeration<URL> enumeration = delegate.getResources(name);
while (enumeration.hasMoreElements()) {
vector.add(enumeration.nextElement()); }}
return vector.elements(); }

}