提交 bf7eb802 编写于 作者: J Jesse Glick

[FIXED JENKINS-16261] Defer JAR copying from MavenComputerListener.preOnline.

上级 594a4e75
......@@ -55,6 +55,9 @@ Upcoming changes</a>
<!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image>
<li class=bug>
Send Maven agent JARs to slaves on demand, not unconditionally upon connection.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-16261">issue 16261</a>)
<li class=bug>
Build number symlinks and permalinks not updated for Maven module builds.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-18846">issue 18846</a>)
......
......@@ -33,6 +33,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;
......@@ -42,6 +43,8 @@ import java.nio.charset.UnsupportedCharsetException;
import java.util.Arrays;
import jenkins.model.Jenkins;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Zip;
import org.kohsuke.stapler.framework.io.IOException2;
......@@ -357,7 +360,7 @@ public abstract class AbstractMavenProcessFactory
args.addTokenized(getMavenOpts());
args.add( "-cp" );
args.add(getMavenAgentClassPath(mvn,isMaster,slaveRoot,listener));
args.add(getMavenAgentClassPath(mvn, slaveRoot, listener));
args.add(getMainClassName());
......@@ -374,9 +377,9 @@ public abstract class AbstractMavenProcessFactory
args.add(remotingJar);
// interceptor.jar
args.add(getMavenInterceptorClassPath(mvn,isMaster,slaveRoot));
args.add(getMavenInterceptorClassPath(mvn, slaveRoot, listener));
String mavenInterceptorCommonClasspath = getMavenInterceptorCommonClassPath( mvn, isMaster, slaveRoot );
String mavenInterceptorCommonClasspath = getMavenInterceptorCommonClassPath(mvn, slaveRoot, listener);
if (mavenInterceptorCommonClasspath!=null){
args.add( mavenInterceptorCommonClasspath );
......@@ -385,7 +388,7 @@ public abstract class AbstractMavenProcessFactory
// TCP/IP port to establish the remoting infrastructure
args.add(tcpPort);
String interceptorOverride = getMavenInterceptorOverride(mvn,isMaster,slaveRoot);
String interceptorOverride = getMavenInterceptorOverride(mvn, slaveRoot, listener);
if (interceptorOverride!=null) {
args.add(interceptorOverride);
}
......@@ -396,25 +399,25 @@ public abstract class AbstractMavenProcessFactory
/**
* Returns the classpath string for the maven-agent jar including classworlds
*/
protected abstract String getMavenAgentClassPath(MavenInstallation mvn,boolean isMaster,FilePath slaveRoot,BuildListener listener) throws IOException, InterruptedException;
protected abstract String getMavenAgentClassPath(MavenInstallation mvn, FilePath slaveRoot,BuildListener listener) throws IOException, InterruptedException;
/**
* Returns the classpath string for the maven-interceptor jar
*/
protected abstract String getMavenInterceptorClassPath(MavenInstallation mvn,boolean isMaster,FilePath slaveRoot) throws IOException, InterruptedException;
protected abstract String getMavenInterceptorClassPath(MavenInstallation mvn, FilePath slaveRoot, BuildListener listener) throws IOException, InterruptedException;
/**
* Returns the classpath string for the maven-interceptor jar
* @since 1.525
*/
protected String getMavenInterceptorCommonClassPath(MavenInstallation mvn,boolean isMaster,FilePath slaveRoot) throws IOException, InterruptedException {
protected String getMavenInterceptorCommonClassPath(MavenInstallation mvn, FilePath slaveRoot, BuildListener listener) throws IOException, InterruptedException {
return null;
}
/**
* For Maven 2.1.x - 2.2.x we need an additional jar which overrides some classes in the other interceptor jar.
*/
protected abstract String getMavenInterceptorOverride(MavenInstallation mvn,boolean isMaster,FilePath slaveRoot) throws IOException, InterruptedException;
protected abstract String getMavenInterceptorOverride(MavenInstallation mvn, FilePath slaveRoot, BuildListener listener) throws IOException, InterruptedException;
/**
* Returns the name of the Maven main class.
......@@ -493,6 +496,54 @@ public abstract class AbstractMavenProcessFactory
}
}
/**
* Copies a Maven-related JAR to the slave on demand.
* Can also be used when run on master.
* @param root the FS root of the slave (null means running on master)
* @param representative a representative class present in the JAR
* @param seedName the basename of the JAR
* @param listener a listener for any problems
* @return the (local or remote) absolute path of the JAR
* @throws IOException in case copying fails
* @throws InterruptedException in case copying is interrupted
* @since 1.530
*/
protected final String classPathEntry(FilePath root, Class<?> representative, String seedName, TaskListener listener) throws IOException, InterruptedException {
if (root == null) { // master
return Which.jarFile(representative).getAbsolutePath();
} else {
return copyJar(listener.getLogger(), root, representative, seedName).getRemote();
}
}
/**
* Copies a jar file from the master to slave.
*/
static FilePath copyJar(PrintStream log, FilePath dst, Class<?> representative, String seedName) throws IOException, InterruptedException {
// in normal execution environment, the master should be loading 'representative' from this jar, so
// in that way we can find it.
File jar = Which.jarFile(representative);
FilePath copiedJar = dst.child(seedName + ".jar");
if (jar.isDirectory()) {
// but during the development and unit test environment, we may be picking the class up from the classes dir
Zip zip = new Zip();
zip.setBasedir(jar);
File t = File.createTempFile(seedName, "jar");
t.delete();
zip.setDestFile(t);
zip.setProject(new Project());
zip.execute();
jar = t;
} else if (copiedJar.lastModified() > jar.lastModified()) {
log.println(seedName + ".jar already up to date");
return copiedJar;
}
new FilePath(jar).copyTo(copiedJar);
log.println("Copied " + seedName + ".jar");
return copiedJar;
}
/**
* Returns the current {@link Node} on which we are buildling.
*/
......
......@@ -29,7 +29,6 @@ import hudson.Launcher;
import hudson.model.BuildListener;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.Which;
import hudson.tasks.Maven.MavenInstallation;
import jenkins.maven3.agent.Maven31Main;
import org.jvnet.hudson.maven3.launcher.Maven31Interceptor;
......@@ -58,9 +57,9 @@ public class Maven31ProcessFactory extends Maven3ProcessFactory
}
@Override
protected String getMavenAgentClassPath(MavenInstallation mvn,boolean isMaster,FilePath slaveRoot,BuildListener listener) throws IOException, InterruptedException {
protected String getMavenAgentClassPath(MavenInstallation mvn, FilePath slaveRoot, BuildListener listener) throws IOException, InterruptedException {
String classWorldsJar = getLauncher().getChannel().call(new Maven3ProcessFactory.GetClassWorldsJar(mvn.getHome(),listener));
String path = (isMaster? Which.jarFile(Maven31Main.class).getAbsolutePath():slaveRoot.child("maven31-agent.jar").getRemote())+
String path = classPathEntry(slaveRoot, Maven31Main.class, "maven31-agent", listener) +
(getLauncher().isUnix()?":":";")+classWorldsJar;
// TODO this configurable??
......@@ -91,20 +90,12 @@ public class Maven31ProcessFactory extends Maven3ProcessFactory
}
@Override
protected String getMavenInterceptorClassPath(MavenInstallation mvn,boolean isMaster,FilePath slaveRoot) throws IOException, InterruptedException {
String path = isMaster?
Which.jarFile(Maven31Interceptor.class).getAbsolutePath():
slaveRoot.child("maven31-interceptor.jar").getRemote();
return path;
protected String getMavenInterceptorClassPath(MavenInstallation mvn, FilePath slaveRoot, BuildListener listener) throws IOException, InterruptedException {
return classPathEntry(slaveRoot, Maven31Interceptor.class, "maven31-interceptor", listener);
}
protected String getMavenInterceptorCommonClassPath(MavenInstallation mvn,boolean isMaster,FilePath slaveRoot) throws IOException, InterruptedException {
String path = isMaster?
Which.jarFile(HudsonMavenExecutionResult.class).getAbsolutePath():
slaveRoot.child("maven3-interceptor-commons.jar").getRemote();
return path;
protected String getMavenInterceptorCommonClassPath(MavenInstallation mvn, FilePath slaveRoot, BuildListener listener) throws IOException, InterruptedException {
return classPathEntry(slaveRoot, HudsonMavenExecutionResult.class, "maven3-interceptor-commons", listener);
}
......
......@@ -31,7 +31,6 @@ import hudson.model.Run.RunnerAbortedException;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.Which;
import hudson.tasks.Maven.MavenInstallation;
import org.jvnet.hudson.maven3.agent.Maven3Main;
import org.jvnet.hudson.maven3.launcher.Maven3Launcher;
......@@ -55,10 +54,10 @@ public class Maven3ProcessFactory extends AbstractMavenProcessFactory implements
}
@Override
protected String getMavenAgentClassPath(MavenInstallation mvn,boolean isMaster,FilePath slaveRoot,BuildListener listener) throws IOException, InterruptedException {
protected String getMavenAgentClassPath(MavenInstallation mvn, FilePath slaveRoot, BuildListener listener) throws IOException, InterruptedException {
String classWorldsJar = getLauncher().getChannel().call(new GetClassWorldsJar(mvn.getHome(),listener));
return (isMaster? Which.jarFile(Maven3Main.class).getAbsolutePath():slaveRoot.child("maven3-agent.jar").getRemote())+
return classPathEntry(slaveRoot, Maven3Main.class, "maven3-agent", listener) +
(getLauncher().isUnix()?":":";")+classWorldsJar;
}
......@@ -68,21 +67,17 @@ public class Maven3ProcessFactory extends AbstractMavenProcessFactory implements
}
@Override
protected String getMavenInterceptorClassPath(MavenInstallation mvn,boolean isMaster,FilePath slaveRoot) throws IOException, InterruptedException {
return isMaster?
Which.jarFile(Maven3Launcher.class).getAbsolutePath():
slaveRoot.child("maven3-interceptor.jar").getRemote();
protected String getMavenInterceptorClassPath(MavenInstallation mvn, FilePath slaveRoot, BuildListener listener) throws IOException, InterruptedException {
return classPathEntry(slaveRoot, Maven3Launcher.class, "maven3-interceptor", listener);
}
protected String getMavenInterceptorCommonClassPath(MavenInstallation mvn,boolean isMaster,FilePath slaveRoot) throws IOException, InterruptedException {
return isMaster?
Which.jarFile(HudsonMavenExecutionResult.class).getAbsolutePath():
slaveRoot.child("maven3-interceptor-commons.jar").getRemote();
protected String getMavenInterceptorCommonClassPath(MavenInstallation mvn, FilePath slaveRoot, BuildListener listener) throws IOException, InterruptedException {
return classPathEntry(slaveRoot, HudsonMavenExecutionResult.class, "maven3-interceptor-commons", listener);
}
@Override
protected String getMavenInterceptorOverride(MavenInstallation mvn,
boolean isMaster, FilePath slaveRoot) throws IOException,
FilePath slaveRoot, BuildListener listener) throws IOException,
InterruptedException {
return null;
}
......
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Stephen Connolly
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.maven;
import hudson.Extension;
import hudson.FilePath;
import hudson.maven.agent.AbortException;
import hudson.maven.agent.Main;
import hudson.maven.agent.Maven21Interceptor;
import hudson.model.Computer;
import hudson.model.TaskListener;
import hudson.remoting.Channel;
import hudson.remoting.Which;
import hudson.slaves.ComputerListener;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Zip;
import org.codehaus.plexus.classworlds.ClassWorld;
import jenkins.maven3.agent.Maven31Agent;
import org.jvnet.hudson.maven3.agent.Maven3Main;
import org.jvnet.hudson.maven3.launcher.Maven31Interceptor;
import org.jvnet.hudson.maven3.launcher.Maven3Launcher;
import org.jvnet.hudson.maven3.listeners.HudsonMavenExecutionResult;
/**
* When a slave is connected, copy <tt>maven-agent.jar</tt> and <tt>maven-intercepter.jar</tt>
*
* @author Kohsuke Kawaguchi
*/
@Extension
public class MavenComputerListener extends ComputerListener {
@Override
public void preOnline(Computer c, Channel channel,FilePath root, TaskListener listener) throws IOException, InterruptedException {
PrintStream logger = listener.getLogger();
copyJar(logger, root, Main.class, "maven-agent");
copyJar(logger, root, Maven3Main.class, "maven3-agent");
copyJar(logger, root, Maven31Agent.class, "maven31-agent");
copyJar(logger, root, Maven3Launcher.class, "maven3-interceptor");
copyJar(logger, root, Maven31Interceptor.class, "maven31-interceptor");
copyJar(logger, root, AbortException.class, "maven-interceptor");
copyJar(logger, root, HudsonMavenExecutionResult.class, "maven3-interceptor-commons" );
copyJar(logger, root, Maven21Interceptor.class, "maven2.1-interceptor");
copyJar(logger, root, ClassWorld.class, "plexus-classworld");
// copy classworlds 1.1 for maven2 builds
// if this line fails during the unit test from IDE, it means you need to "mvn compile" maven-plugin
root.child( "classworlds.jar" ).copyFrom(getClass().getClassLoader().getResource("classworlds.jar"));
logger.println("Copied classworlds.jar");
}
/**
* Copies a jar file from the master to slave.
*/
private void copyJar(PrintStream log, FilePath dst, Class<?> representative, String seedName) throws IOException, InterruptedException {
// in normal execution environment, the master should be loading 'representative' from this jar, so
// in that way we can find it.
File jar = Which.jarFile(representative);
if(jar.isDirectory()) {
// but during the development and unit test environment, we may be picking the class up from the classes dir
Zip zip = new Zip();
zip.setBasedir(jar);
File t = File.createTempFile(seedName, "jar");
t.delete();
zip.setDestFile(t);
zip.setProject(new Project());
zip.execute();
jar = t;
}
new FilePath(jar).copyTo(dst.child(seedName +".jar"));
log.println("Copied "+seedName+".jar");
}
}
......@@ -33,13 +33,14 @@ import hudson.model.Run.RunnerAbortedException;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.Which;
import hudson.tasks.Maven.MavenInstallation;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
/**
......@@ -55,13 +56,33 @@ final class MavenProcessFactory extends AbstractMavenProcessFactory implements P
}
@Override
protected String getMavenAgentClassPath(MavenInstallation mvn,boolean isMaster,FilePath slaveRoot,BuildListener listener) throws IOException, InterruptedException {
String classWorldsJar = getLauncher().getChannel().call(new GetClassWorldsJar(mvn.getHome(),listener));
protected String getMavenAgentClassPath(MavenInstallation mvn, FilePath slaveRoot, BuildListener listener) throws IOException, InterruptedException {
String classPath =
( isMaster ? Which.jarFile( Main.class ).getAbsolutePath()
: slaveRoot.child( "maven-agent.jar" ).getRemote() )
+ ( getLauncher().isUnix() ? ":" : ";" )
+ ( isMaster ? classWorldsJar : slaveRoot.child( "classworlds.jar" ).getRemote() );
classPathEntry(slaveRoot, Main.class, "maven-agent", listener)
+ ( getLauncher().isUnix() ? ":" : ";" );
if (slaveRoot == null) { // master
String classWorldsJar = getLauncher().getChannel().call(new GetClassWorldsJar(mvn.getHome(),listener));
classPath += classWorldsJar;
} else {
// copy classworlds 1.1 for maven2 builds
// if this line fails during the unit test from IDE, it means you need to "mvn compile" maven-plugin
// TODO why would we not pick it up using GetClassWorldsJar like we do for M2 on master or M3 anywhere?
FilePath jar = slaveRoot.child("classworlds.jar");
// copied to root of this JAR using dependency:generate-resources:
URLConnection conn = MavenProcessFactory.class.getClassLoader().getResource("classworlds.jar").openConnection();
if (jar.lastModified() > conn.getLastModified()) {
listener.getLogger().println("classworlds.jar already up to date");
} else {
InputStream in = conn.getInputStream();
try {
jar.copyFrom(in);
} finally {
in.close();
}
listener.getLogger().println("Copied classworlds.jar");
}
classPath += jar.getRemote();
}
return classPath;
}
......@@ -70,20 +91,16 @@ final class MavenProcessFactory extends AbstractMavenProcessFactory implements P
}
@Override
protected String getMavenInterceptorClassPath(MavenInstallation mvn,boolean isMaster,FilePath slaveRoot) throws IOException, InterruptedException {
return isMaster?
Which.jarFile(hudson.maven.agent.AbortException.class).getAbsolutePath():
slaveRoot.child("maven-interceptor.jar").getRemote();
protected String getMavenInterceptorClassPath(MavenInstallation mvn, FilePath slaveRoot, BuildListener listener) throws IOException, InterruptedException {
return classPathEntry(slaveRoot, hudson.maven.agent.AbortException.class, "maven-interceptor", listener);
}
@Override
protected String getMavenInterceptorOverride(MavenInstallation mvn,
boolean isMaster, FilePath slaveRoot) throws IOException,
FilePath slaveRoot, BuildListener listener) throws IOException,
InterruptedException {
if(mvn.isMaven2_1(getLauncher())) {
return isMaster?
Which.jarFile(Maven21Interceptor.class).getAbsolutePath():
slaveRoot.child("maven2.1-interceptor.jar").getRemote();
return classPathEntry(slaveRoot, Maven21Interceptor.class, "maven2.1-interceptor", listener);
}
return null;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册