File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\maven-plugin\src\main\java\hudson\maven\ProcessCache.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 package hudson.maven;
25
26 import hudson.Util;
27 import hudson.Proc;
28 import hudson.model.BuildListener;
29 import hudson.model.JDK;
30 import hudson.model.TaskListener;
31 import hudson.remoting.Callable;
32 import hudson.remoting.Channel;
33 import hudson.remoting.VirtualChannel;
34 import hudson.remoting.RequestAbortedException;
35 import hudson.tasks.Maven.MavenInstallation;
36 import hudson.util.DelegatingOutputStream;
37 import hudson.util.NullStream;
38
39 import java.io.IOException;
40 import java.io.OutputStream;
41 import java.util.Iterator;
42 import java.util.LinkedList;
43 import java.util.Map;
44 import java.util.Properties;
45 import java.util.WeakHashMap;
46 import java.util.logging.Logger;
47 import java.util.logging.Level;
48
49 /**
50 * Hold on to launched Maven processes so that multiple builds
51 * can reuse the same Maven JVM, which leads to improved performance.
52 *
53 * @author Kohsuke Kawaguchi
54 */
55 public final class ProcessCache {
56 /**
57 * Implemented by the caller to create a new process
58 * (when a new one is needed.)
59 */
60 interface Factory {
61 /**
62 * @param out
63 * The output from the process should be sent to this output stream.
64 */
65 NewProcess newProcess(BuildListener listener,OutputStream out) throws IOException, InterruptedException;
66 String getMavenOpts();
67 MavenInstallation getMavenInstallation(TaskListener listener) throws IOException, InterruptedException;
68 JDK getJava(TaskListener listener) throws IOException, InterruptedException;
69 }
70
71 public static class NewProcess {
72 public final Channel channel;
73 public final Proc proc;
74
75 public NewProcess(Channel channel, Proc proc) {
76 this.channel = channel;
77 this.proc = proc;
78 }
79 }
80
81 class MavenProcess {
82 /**
83 * Channel connected to the maven process.
84 */
85 final Channel channel;
86 /**
87 * MAVEN_OPTS of this VM.
88 */
89 private final String mavenOpts;
90 private final PerChannel parent;
91 final Proc proc;
92 private final MavenInstallation installation;
93 private final JDK jdk;
94 private final RedirectableOutputStream output;
95 /**
96 * System properties captured right after the process is created.
97 * Each time the process is reused, the system properties are reset,
98 * since Maven corrupts them as a side-effect of the build.
99 */
100 private final Properties systemProperties;
101
102 private int age = 0;
103
104 MavenProcess(PerChannel parent, String mavenOpts, MavenInstallation installation, JDK jdk, NewProcess np, RedirectableOutputStream output) throws IOException, InterruptedException {
105 this.parent = parent;
106 this.mavenOpts = mavenOpts;
107 this.channel = np.channel;
108 this.proc = np.proc;
109 this.installation = installation;
110 this.jdk = jdk;
111 this.output = output;
112 this.systemProperties = channel.call(new GetSystemProperties());
113 }
114
115 public String getMavenOpts() {
116 return mavenOpts;
117 }
118
119 boolean matches(String mavenOpts,MavenInstallation installation, JDK jdk) {
120 return Util.fixNull(this.mavenOpts).equals(Util.fixNull(mavenOpts))
121 && this.installation==installation
122 && this.jdk==jdk;
123 }
124
125 public void recycle() throws IOException {
126 if(age>=MAX_AGE || maxProcess==0)
127 discard();
128 else {
129 output.set(new NullStream());
130 // make room for the new process and reuse.
131 synchronized(parent.processes) {
132 while(parent.processes.size()>=maxProcess)
133 parent.processes.removeFirst().discard();
134 parent.processes.add(this);
135 }
136 }
137 }
138
139 /**
140 * Discards this maven process.
141 * It won't be reused in future builds.
142 */
143 public void discard() {
144 try {
145 channel.close();
146 } catch (IOException e) {
147 LOGGER.log(Level.WARNING,"Failed to discard the maven process orderly",e);
148 }
149 }
150
151 /**
152 * Calls a {@link Callable} on the channel, with additional error diagnostics.
153 */
154 public <V,T extends Throwable> V call(Callable<V,T> callable) throws T, IOException, InterruptedException {
155 try {
156 return channel.call(callable);
157 } catch (RequestAbortedException e) {
158 // this is normally triggered by the unexpected Maven JVM termination.
159 // check if the process is still alive, after giving it a bit of time to die
160 Thread.sleep(1000);
161 if(proc.isAlive())
162 throw e; // it's still alive. treat this as a bug in the code
163 else {
164 String msg = "Maven JVM terminated unexpectedly with exit code " + proc.join();
165 LOGGER.log(Level.FINE,msg,e);
166 throw new hudson.AbortException(msg);
167 }
168 }
169 }
170 }
171
172 static class PerChannel {
173 /**
174 * Cached processes.
175 */
176 private final LinkedList<MavenProcess> processes = new LinkedList<MavenProcess>();
177 }
178
179 // use WeakHashMap to avoid keeping VirtualChannel in memory.
180 private final Map<VirtualChannel,PerChannel> cache = new WeakHashMap<VirtualChannel,PerChannel>();
181 private final int maxProcess;
182
183 /**
184 * @param maxProcess
185 * Number of maximum processes to cache.
186 */
187 protected ProcessCache(int maxProcess) {
188 this.maxProcess = maxProcess;
189 }
190
191 private synchronized PerChannel get(VirtualChannel owner) {
192 PerChannel r = cache.get(owner);
193 if(r==null)
194 cache.put(owner,r=new PerChannel());
195 return r;
196 }
197
198 /**
199 * Gets or creates a new maven process for launch.
200 */
201 public MavenProcess get(VirtualChannel owner, BuildListener listener, Factory factory) throws InterruptedException, IOException {
202 String mavenOpts = factory.getMavenOpts();
203 MavenInstallation installation = factory.getMavenInstallation(listener);
204 JDK jdk = factory.getJava(listener);
205
206 PerChannel list = get(owner);
207 synchronized(list.processes) {
208 for (Iterator<MavenProcess> itr = list.processes.iterator(); itr.hasNext();) {
209 MavenProcess p = itr.next();
210 if(p.matches(mavenOpts,installation,jdk)) {
211 // reset the system property.
212 // this also serves as the sanity check.
213 try {
214 p.call(new SetSystemProperties(p.systemProperties));
215 } catch (IOException e) {
216 p.discard();
217 itr.remove();
218 continue;
219 }
220
221 listener.getLogger().println(Messages.ProcessCache_Reusing());
222 itr.remove();
223 p.age++;
224 p.output.set(listener.getLogger());
225 return p;
226 }
227 }
228 }
229
230 RedirectableOutputStream out = new RedirectableOutputStream(listener.getLogger());
231 return new MavenProcess(list,mavenOpts,installation,jdk,factory.newProcess(listener,out),out);
232 }
233
234
235
236 public static int MAX_AGE = 5;
237
238 static {
239 String age = System.getProperty(ProcessCache.class.getName() + ".age");
240 if(age!=null)
241 MAX_AGE = Integer.parseInt(age);
242 }
243
244 /**
245 * Noop callable used for checking the sanity of the maven process in the cache.
246 */
247 private static class SetSystemProperties implements Callable<Object,RuntimeException> {
248 private final Properties properties;
249
250 public SetSystemProperties(Properties properties) {
251 this.properties = properties;
252 }
253
254 public Object call() {
255 System.setProperties(properties);
256 return null;
257 }
258 private static final long serialVersionUID = 1L;
259 }
260
261 private static class GetSystemProperties implements Callable<Properties,RuntimeException> {
262 public Properties call() {
263 return System.getProperties();
264 }
265 private static final long serialVersionUID = 1L;
266 }
267
268 static class RedirectableOutputStream extends DelegatingOutputStream {
269 public RedirectableOutputStream(OutputStream out) {
270 super(out);
271 }
272
273 public void set(OutputStream os) {
274 super.out = os;
275 }
276 }
277
278 private static final Logger LOGGER = Logger.getLogger(ProcessCache.class.getName());
279 }
280