File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\model\DownloadService.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.model;
25
26 import hudson.Extension;
27 import hudson.ExtensionList;
28 import hudson.ExtensionPoint;
29 import hudson.util.FormValidation;
30 import hudson.util.FormValidation.Kind;
31 import hudson.util.IOException2;
32 import hudson.util.IOUtils;
33 import hudson.util.QuotedStringTokenizer;
34 import hudson.util.TextFile;
35 import jenkins.model.Jenkins;
36 import jenkins.util.JSONSignatureValidator;
37 import net.sf.json.JSONException;
38 import org.kohsuke.stapler.Stapler;
39
40 import java.io.File;
41 import java.io.IOException;
42 import java.util.logging.Logger;
43
44 import net.sf.json.JSONObject;
45 import org.kohsuke.stapler.StaplerRequest;
46 import org.kohsuke.stapler.StaplerResponse;
47
48 import static hudson.util.TimeUnit2.DAYS;
49
50 /**
51 * Service for plugins to periodically retrieve update data files
52 * (like the one in the update center) through browsers.
53 *
54 * <p>
55 * Because the retrieval of the file goes through XmlHttpRequest,
56 * we cannot reliably pass around binary.
57 *
58 * @author Kohsuke Kawaguchi
59 */
60 @Extension
61 public class DownloadService extends PageDecorator {
62 /**
63 * Builds up an HTML fragment that starts all the download jobs.
64 */
65 public String generateFragment() {
66 if (neverUpdate) return "";
67 if (doesNotSupportPostMessage()) return "";
68
69 StringBuilder buf = new StringBuilder();
70 if(Jenkins.getInstance().hasPermission(Jenkins.READ)) {
71 long now = System.currentTimeMillis();
72 for (Downloadable d : Downloadable.all()) {
73 if(d.getDue()<now && d.lastAttempt+10*1000<now) {
74 buf.append("<script>")
75 .append("Behaviour.addLoadEvent(function() {")
76 .append(" downloadService.download(")
77 .append(QuotedStringTokenizer.quote(d.getId()))
78 .append(',')
79 .append(QuotedStringTokenizer.quote(mapHttps(d.getUrl())))
80 .append(',')
81 .append("{version:"+QuotedStringTokenizer.quote(Jenkins.VERSION)+'}')
82 .append(',')
83 .append(QuotedStringTokenizer.quote(Stapler.getCurrentRequest().getContextPath()+'/'+getUrl()+"/byId/"+d.getId()+"/postBack"))
84 .append(',')
85 .append("null);")
86 .append("});")
87 .append("</script>");
88 d.lastAttempt = now;
89 }
90 }
91 }
92 return buf.toString();
93 }
94
95 private boolean doesNotSupportPostMessage() {
96 StaplerRequest req = Stapler.getCurrentRequest();
97 if (req==null) return false;
98
99 String ua = req.getHeader("User-Agent");
100 if (ua==null) return false;
101
102 // according to http://caniuse.com/#feat=x-doc-messaging, IE <=7 doesn't support pstMessage
103 // see http://www.useragentstring.com/pages/Internet%20Explorer/ for user agents
104
105 // we want to err on the cautious side here.
106 // Because of JENKINS-15105, we can't serve signed metadata from JSON, which means we need to be
107 // using a modern browser as a vehicle to request these data. This check is here to prevent Jenkins
108 // from using older browsers that are known not to support postMessage as the vehicle.
109 return ua.contains("Windows") && (ua.contains(" MSIE 5.") || ua.contains(" MSIE 6.") || ua.contains(" MSIE 7."));
110 }
111
112 private String mapHttps(String url) {
113 /*
114 HACKISH:
115
116 Loading scripts in HTTP from HTTPS pages cause browsers to issue a warning dialog.
117 The elegant way to solve the problem is to always load update center from HTTPS,
118 but our backend mirroring scheme isn't ready for that. So this hack serves regular
119 traffic in HTTP server, and only use HTTPS update center for Jenkins in HTTPS.
120
121 We'll monitor the traffic to see if we can sustain this added traffic.
122 */
123 if (url.startsWith("http://updates.jenkins-ci.org/") && Jenkins.getInstance().isRootUrlSecure())
124 return "https"+url.substring(4);
125 return url;
126 }
127
128 /**
129 * Gets {@link Downloadable} by its ID.
130 * Used to bind them to URL.
131 */
132 public Downloadable getById(String id) {
133 for (Downloadable d : Downloadable.all())
134 if(d.getId().equals(id))
135 return d;
136 return null;
137 }
138
139 /**
140 * Represents a periodically updated JSON data file obtained from a remote URL.
141 *
142 * <p>
143 * This mechanism is one of the basis of the update center, which involves fetching
144 * up-to-date data file.
145 *
146 * @since 1.305
147 */
148 public static class Downloadable implements ExtensionPoint {
149 private final String id;
150 private final String url;
151 private final long interval;
152 private volatile long due=0;
153 private volatile long lastAttempt=Long.MIN_VALUE;
154
155 /**
156 *
157 * @param url
158 * URL relative to {@link UpdateCenter#getDefaultBaseUrl()}.
159 * So if this string is "foo.json", the ultimate URL will be
160 * something like "http://updates.jenkins-ci.org/updates/foo.json"
161 *
162 * For security and privacy reasons, we don't allow the retrieval
163 * from random locations.
164 */
165 public Downloadable(String id, String url, long interval) {
166 this.id = id;
167 this.url = url;
168 this.interval = interval;
169 }
170
171 public Downloadable() {
172 this.id = getClass().getName().replace('$','.');
173 this.url = this.id+".json";
174 this.interval = DEFAULT_INTERVAL;
175 }
176
177 /**
178 * Uses the class name as an ID.
179 */
180 public Downloadable(Class id) {
181 this(id.getName().replace('$','.'));
182 }
183
184 public Downloadable(String id) {
185 this(id,id+".json");
186 }
187
188 public Downloadable(String id, String url) {
189 this(id,url, DEFAULT_INTERVAL);
190 }
191
192 public String getId() {
193 return id;
194 }
195
196 /**
197 * URL to download.
198 */
199 public String getUrl() {
200 return Jenkins.getInstance().getUpdateCenter().getDefaultBaseUrl()+"updates/"+url;
201 }
202
203 /**
204 * How often do we retrieve the new image?
205 *
206 * @return
207 * number of milliseconds between retrieval.
208 */
209 public long getInterval() {
210 return interval;
211 }
212
213 /**
214 * This is where the retrieved file will be stored.
215 */
216 public TextFile getDataFile() {
217 return new TextFile(new File(Jenkins.getInstance().getRootDir(),"updates/"+id));
218 }
219
220 /**
221 * When shall we retrieve this file next time?
222 */
223 public long getDue() {
224 if(due==0)
225 // if the file doesn't exist, this code should result
226 // in a very small (but >0) due value, which should trigger
227 // the retrieval immediately.
228 due = getDataFile().file.lastModified()+interval;
229 return due;
230 }
231
232 /**
233 * Loads the current file into JSON and returns it, or null
234 * if no data exists.
235 */
236 public JSONObject getData() throws IOException {
237 TextFile df = getDataFile();
238 if(df.exists())
239 try {
240 return JSONObject.fromObject(df.read());
241 } catch (JSONException e) {
242 df.delete(); // if we keep this file, it will cause repeated failures
243 throw new IOException2("Failed to parse "+df+" into JSON",e);
244 }
245 return null;
246 }
247
248 /**
249 * This is where the browser sends us the data.
250 */
251 public void doPostBack(StaplerRequest req, StaplerResponse rsp) throws IOException {
252 long dataTimestamp = System.currentTimeMillis();
253 due = dataTimestamp+getInterval(); // success or fail, don't try too often
254
255 String json = IOUtils.toString(req.getInputStream(),"UTF-8");
256 JSONObject o = JSONObject.fromObject(json);
257
258 if (signatureCheck) {
259 FormValidation e = new JSONSignatureValidator("downloadable '"+id+"'").verifySignature(o);
260 if (e.kind!= Kind.OK) {
261 LOGGER.severe(e.renderHtml());
262 throw e;
263 }
264 }
265
266 TextFile df = getDataFile();
267 df.write(json);
268 df.file.setLastModified(dataTimestamp);
269 LOGGER.info("Obtained the updated data file for "+id);
270
271 rsp.setContentType("text/plain"); // So browser won't try to parse response
272 }
273
274 /**
275 * Returns all the registered {@link Downloadable}s.
276 */
277 public static ExtensionList<Downloadable> all() {
278 return Jenkins.getInstance().getExtensionList(Downloadable.class);
279 }
280
281 /**
282 * Returns the {@link Downloadable} that has the given ID.
283 */
284 public static Downloadable get(String id) {
285 for (Downloadable d : all()) {
286 if(d.id.equals(id))
287 return d;
288 }
289 return null;
290 }
291
292 private static final Logger LOGGER = Logger.getLogger(Downloadable.class.getName());
293 private static final long DEFAULT_INTERVAL =
294 Long.getLong(Downloadable.class.getName()+".defaultInterval", DAYS.toMillis(1));
295 }
296
297 public static boolean neverUpdate = Boolean.getBoolean(DownloadService.class.getName()+".never");
298
299 /**
300 * Off by default until we know this is reasonably working.
301 */
302 public static boolean signatureCheck = !Boolean.getBoolean(DownloadService.class.getName()+".noSignatureCheck");
303 }
304
305