1 19 20 package org.netbeans.nbbuild; 21 22 import java.io.File ; 23 import java.io.FileWriter ; 24 import java.io.IOException ; 25 import java.io.InputStream ; 26 import java.io.StringWriter ; 27 import java.io.Writer ; 28 import java.util.ArrayList ; 29 import java.util.Collections ; 30 import java.util.Enumeration ; 31 import java.util.HashMap ; 32 import java.util.HashSet ; 33 import java.util.Iterator ; 34 import java.util.List ; 35 import java.util.Map ; 36 import java.util.Properties ; 37 import java.util.Set ; 38 import java.util.StringTokenizer ; 39 import java.util.jar.JarEntry ; 40 import java.util.jar.JarFile ; 41 import java.util.jar.Manifest ; 42 import java.util.regex.Matcher ; 43 import java.util.regex.Pattern ; 44 import java.util.zip.ZipEntry ; 45 import javax.xml.parsers.ParserConfigurationException ; 46 import org.apache.tools.ant.BuildException; 47 import org.apache.tools.ant.DirectoryScanner; 48 import org.apache.tools.ant.Project; 49 import org.apache.tools.ant.Task; 50 import org.apache.tools.ant.taskdefs.Copy; 51 import org.apache.tools.ant.taskdefs.SignJar; 52 import org.apache.tools.ant.taskdefs.Zip; 53 import org.apache.tools.ant.types.FileSet; 54 import org.apache.tools.ant.types.ZipFileSet; 55 import org.xml.sax.SAXException ; 56 57 61 public class MakeJNLP extends Task { 62 63 private FileSet files; 64 private SignJar signTask; 65 66 public FileSet createModules() 67 throws BuildException { 68 if (files != null) throw new BuildException("modules can be created just once"); 69 files = new FileSet(); 70 return files; 71 } 72 73 private SignJar getSignTask() { 74 if (signTask == null) { 75 signTask = (SignJar)getProject().createTask("signjar"); 76 } 77 return signTask; 78 } 79 80 private File target; 81 public void setDir(File t) { 82 target = t; 83 } 84 85 public void setAlias(String a) { 86 getSignTask().setAlias(a); 87 } 88 89 public void setStorePass(String p) { 90 getSignTask().setStorepass(p); 91 } 92 93 public void setKeystore(String k) { 94 getSignTask().setKeystore(k); 95 } 96 97 private String codebase = "$$codebase"; 98 public void setCodebase(String s) { 99 this.codebase = s; 100 } 101 102 private boolean verify; 103 public void setVerify(boolean v) { 104 this.verify = v; 105 } 106 107 private String verifyExcludes; 108 111 public void setVerifyExcludes(String s) { 112 this.verifyExcludes = s; 113 } 114 115 private String permissions = "<all-permissions/>"; 116 120 public void setPermissions(String s) { 121 permissions = s; 122 } 123 124 private FileSet indirectJars; 125 133 public void addIndirectJars(FileSet fs) { 134 indirectJars = fs; 135 } 136 137 public void execute() throws BuildException { 138 if (target == null) throw new BuildException("Output dir must be provided"); 139 if (files == null) throw new BuildException("modules must be provided"); 140 141 try { 142 generateFiles(); 143 } catch (IOException ex) { 144 throw new BuildException(ex); 145 } 146 } 147 148 private void generateFiles() throws IOException , BuildException { 149 Set <String > indirectJarPaths = Collections.emptySet(); 150 if (indirectJars != null) { 151 DirectoryScanner scan = indirectJars.getDirectoryScanner(getProject()); 152 indirectJarPaths = new HashSet <String >(); 153 for (String f : scan.getIncludedFiles()) { 154 indirectJarPaths.add(f.replace(File.pathSeparatorChar, '/')); 155 } 156 } 157 158 DirectoryScanner scan = files.getDirectoryScanner(getProject()); 159 for (String f : scan.getIncludedFiles()) { 160 File jar = new File (files.getDir(getProject()), f); 161 162 if (!jar.canRead()) { 163 throw new BuildException("Cannot read file: " + jar); 164 } 165 166 JarFile theJar = new JarFile (jar); 167 String codenamebase = theJar.getManifest().getMainAttributes().getValue("OpenIDE-Module"); 168 if (codenamebase == null) { 169 throw new BuildException("Not a NetBeans Module: " + jar); 170 } 171 { 172 int slash = codenamebase.indexOf('/'); 173 if (slash >= 0) { 174 codenamebase = codenamebase.substring(0, slash); 175 } 176 } 177 String dashcnb = codenamebase.replace('.', '-'); 178 179 String title; 180 String oneline; 181 String shrt; 182 183 { 184 String bundle = theJar.getManifest().getMainAttributes().getValue("OpenIDE-Module-Localizing-Bundle"); 185 Properties prop = new Properties (); 186 if (bundle != null) { 187 ZipEntry en = theJar.getEntry(bundle); 188 if (en == null) { 189 throw new BuildException("Cannot find entry: " + bundle + " in file: " + jar); 190 } 191 InputStream is = theJar.getInputStream(en); 192 prop.load(is); 193 is.close(); 194 } 195 title = prop.getProperty("OpenIDE-Module-Name", codenamebase); 196 oneline = prop.getProperty("OpenIDE-Module-Short-Description", title); 197 shrt = prop.getProperty("OpenIDE-Module-Long-Description", oneline); 198 } 199 200 Map <String ,List <File >> localizedFiles = verifyExtensions(jar, theJar.getManifest(), dashcnb, codenamebase, verify, indirectJarPaths); 201 202 new File (target, dashcnb).mkdir(); 203 204 File signed = new File (new File (target, dashcnb), jar.getName()); 205 File jnlp = new File (target, dashcnb + ".jnlp"); 206 207 StringWriter writeJNLP = new StringWriter (); 208 writeJNLP.write("<?xml version='1.0' encoding='UTF-8'?>\n"); 209 writeJNLP.write("<jnlp spec='1.0+' codebase='" + codebase + "' >\n"); 210 writeJNLP.write(" <information>\n"); 211 writeJNLP.write(" <title>" + title + "</title>\n"); 212 writeJNLP.write(" <vendor>NetBeans</vendor>\n"); 213 writeJNLP.write(" <description kind='one-line'>" + oneline + "</description>\n"); 214 writeJNLP.write(" <description kind='short'>" + shrt + "</description>\n"); 215 writeJNLP.write(" </information>\n"); 216 writeJNLP.write(permissions +"\n"); 217 writeJNLP.write(" <resources>\n"); 218 writeJNLP.write(" <jar HREF='" + dashcnb + '/' + jar.getName() + "'/>\n"); 219 220 processExtensions(jar, theJar.getManifest(), writeJNLP, dashcnb, codebase); 221 processIndirectJars(writeJNLP, dashcnb, codebase); 222 223 writeJNLP.write(" </resources>\n"); 224 225 { 226 for (Map.Entry <String ,List <File >> e : localizedFiles.entrySet()) { 228 String locale = e.getKey(); 229 List <File > files = e.getValue(); 230 231 writeJNLP.write(" <resources locale='" + locale + "'>\n"); 232 233 for (File n : files) { 234 String name = n.getName(); 235 String clusterRootPrefix = jar.getParent() + File.separatorChar; 236 String absname = n.getAbsolutePath(); 237 if (absname.startsWith(clusterRootPrefix)) { 238 name = absname.substring(clusterRootPrefix.length()).replace('/', '-'); 239 } 240 File t = new File (new File (target, dashcnb), name); 241 242 getSignTask().setJar(n); 243 getSignTask().setSignedjar(t); 244 getSignTask().execute(); 245 246 writeJNLP.write(" <jar HREF='" + dashcnb + '/' + name + "'/>\n"); 247 } 248 249 writeJNLP.write(" </resources>\n"); 250 251 } 252 } 253 254 writeJNLP.write(" <component-desc/>\n"); 255 writeJNLP.write("</jnlp>\n"); 256 writeJNLP.close(); 257 258 FileWriter w = new FileWriter (jnlp); 259 w.write(writeJNLP.toString()); 260 w.close(); 261 262 getSignTask().setJar(jar); 263 getSignTask().setSignedjar(signed); 264 getSignTask().execute(); 265 266 267 theJar.close(); 268 } 269 270 } 271 272 private Map <String ,List <File >> verifyExtensions(File f, Manifest mf, String dashcnb, String codebasename, boolean verify, Set <String > indirectJarPaths) throws IOException , BuildException { 273 Map <String ,List <File >> localizedFiles = new HashMap <String ,List <File >>(); 274 275 276 File clusterRoot = f.getParentFile(); 277 String moduleDirPrefix = ""; 278 File updateTracking; 279 for(;;) { 280 updateTracking = new File (clusterRoot, "update_tracking"); 281 if (updateTracking.isDirectory()) { 282 break; 283 } 284 moduleDirPrefix = clusterRoot.getName() + "/" + moduleDirPrefix; 285 clusterRoot = clusterRoot.getParentFile(); 286 if (clusterRoot == null || !clusterRoot.exists()) { 287 if (!verify) { 288 return localizedFiles; 289 } 290 291 throw new BuildException("Cannot find update_tracking directory for module " + f); 292 } 293 } 294 295 File ut = new File (updateTracking, dashcnb + ".xml"); 296 if (!ut.exists()) { 297 throw new BuildException("The file " + ut + " for module " + codebasename + " cannot be found"); 298 } 299 300 Map <String ,String > fileToOwningModule = new HashMap <String ,String >(); 301 try { 302 ModuleSelector.readUpdateTracking(getProject(), ut.toString(), fileToOwningModule); 303 } catch (IOException ex) { 304 throw new BuildException(ex); 305 } catch (ParserConfigurationException ex) { 306 throw new BuildException(ex); 307 } catch (SAXException ex) { 308 throw new BuildException(ex); 309 } 310 311 log("project files: " + fileToOwningModule, Project.MSG_DEBUG); 312 String name = relative(f, clusterRoot); 313 log(" removing: " + name, Project.MSG_DEBUG); 314 removeWithLocales(fileToOwningModule, name, clusterRoot, localizedFiles); 315 name = "config/Modules/" + dashcnb + ".xml"; 316 log(" removing: " + name, Project.MSG_DEBUG); 317 removeWithLocales(fileToOwningModule, name, clusterRoot, localizedFiles); 318 name = "config/ModuleAutoDeps/" + dashcnb + ".xml"; 319 log(" removing: " + name, Project.MSG_DEBUG); 320 removeWithLocales(fileToOwningModule, name, clusterRoot, localizedFiles); 321 name = "update_tracking/" + dashcnb + ".xml"; 322 log(" removing: " + name, Project.MSG_DEBUG); 323 removeWithLocales(fileToOwningModule, name, clusterRoot, localizedFiles); 324 325 326 327 328 String path = mf.getMainAttributes().getValue("Class-Path"); 329 if (path != null) { 330 StringTokenizer tok = new StringTokenizer (path, ", "); 331 while(tok.hasMoreElements()) { 332 String s = tok.nextToken(); 333 File e = new File (f.getParentFile(), s); 334 String r = relative(e, clusterRoot); 335 removeWithLocales(fileToOwningModule, r, clusterRoot, localizedFiles); 336 } 337 } 338 339 fileToOwningModule.remove("ant/nblib/" + dashcnb + ".jar"); 340 341 fileToOwningModule.keySet().removeAll(indirectJarPaths); 342 343 if (verifyExcludes != null) { 344 StringTokenizer tok = new StringTokenizer (verifyExcludes, ", "); 345 while(tok.hasMoreElements()) { 346 removeWithLocales(fileToOwningModule, tok.nextToken(), clusterRoot, localizedFiles); 347 } 348 } 349 350 351 if (verify) { 352 if (!fileToOwningModule.isEmpty()) { 353 throw new BuildException( 354 "Cannot build JNLP for module " + f + " as these files are in " + 355 "module's NBM, but are not referenced from any path:\n" + fileToOwningModule.keySet() 356 ); 357 } 358 } 359 360 return localizedFiles; 361 } 362 363 private static void removeWithLocales(Map <String ,String > removeFrom, String removeWhat, File clusterRoot, Map <String ,List <File >> recordLocales) { 364 if (removeFrom.remove(removeWhat) != null && removeWhat.endsWith(".jar")) { 365 int basedir = removeWhat.lastIndexOf('/'); 366 String base = basedir == -1 ? "" : removeWhat.substring(0, basedir); 367 String name = removeWhat.substring(basedir + 1, removeWhat.length() - 4); 368 Pattern p = Pattern.compile(base + "/locale/" + name + "(|_[a-zA-Z0-9_]+)\\.jar"); 369 370 Iterator it = removeFrom.keySet().iterator(); 371 while (it.hasNext()) { 372 String s = (String )it.next(); 373 Matcher m = p.matcher(s); 374 if (m.matches()) { 375 String locale = m.group(1).substring(1); 376 377 List <File > l = recordLocales.get(locale); 378 if (l == null) { 379 l = new ArrayList <File >(); 380 recordLocales.put(locale, l); 381 } 382 l.add(new File (clusterRoot, s.replace('/', File.separatorChar))); 383 it.remove(); 384 } 385 } 386 } 387 } 388 389 private void processExtensions(File f, Manifest mf, Writer fileWriter, String dashcnb, String codebase) throws IOException , BuildException { 390 391 File nblibJar = new File (new File (new File (f.getParentFile().getParentFile(), "ant"), "nblib"), dashcnb + ".jar"); 392 if (nblibJar.isFile()) { 393 File ext = new File (new File (target, dashcnb), "ant-nblib-" + nblibJar.getName()); 394 fileWriter.write(" <jar HREF='" + dashcnb + '/' + ext.getName() + "'/>\n"); 395 getSignTask().setJar(nblibJar); 396 getSignTask().setSignedjar(ext); 397 getSignTask().execute(); 398 } 399 400 String path = mf.getMainAttributes().getValue("Class-Path"); 401 if (path == null) { 402 return; 403 } 404 405 StringTokenizer tok = new StringTokenizer (path, ", "); 406 while(tok.hasMoreElements()) { 407 String s = tok.nextToken(); 408 409 File e = new File (f.getParentFile(), s); 410 if (!e.canRead()) { 411 throw new BuildException("Cannot read extension " + e + " referenced from " + f); 412 } 413 String n = e.getName(); 414 if (n.endsWith(".jar")) { 415 n = n.substring(0, n.length() - 4); 416 } 417 418 if (isSigned(e) != null) { 419 Copy copy = (Copy)getProject().createTask("copy"); 420 copy.setFile(e); 421 File t = new File (new File (target, dashcnb), s.replace('/', '-')); 422 copy.setTofile(t); 423 copy.execute(); 424 425 String extJnlpName = t.getName().replaceFirst("\\.jar$", "") + ".jnlp"; 426 File jnlp = new File (new File (target, dashcnb), extJnlpName); 427 428 FileWriter writeJNLP = new FileWriter (jnlp); 429 writeJNLP.write("<?xml version='1.0' encoding='UTF-8'?>\n"); 430 writeJNLP.write("<jnlp spec='1.0+' codebase='" + codebase + "' >\n"); 431 writeJNLP.write(" <information>\n"); 432 writeJNLP.write(" <title>" + n + "</title>\n"); 433 writeJNLP.write(" <vendor>NetBeans</vendor>\n"); 434 writeJNLP.write(" </information>\n"); 435 writeJNLP.write(permissions +"\n"); 436 writeJNLP.write(" <resources>\n"); 437 writeJNLP.write(" <jar HREF='" + dashcnb + '/' + t.getName() + "'/>\n"); 438 writeJNLP.write(" </resources>\n"); 439 writeJNLP.write(" <component-desc/>\n"); 440 writeJNLP.write("</jnlp>\n"); 441 writeJNLP.close(); 442 443 fileWriter.write(" <extension name='" + e.getName().replaceFirst("\\.jar$", "") + "' HREF='" + dashcnb + '/' + extJnlpName + "'/>\n"); 444 } else { 445 File ext = new File (new File (target, dashcnb), s.replace('/', '-')); 446 447 fileWriter.write(" <jar HREF='" + dashcnb + '/' + ext.getName() + "'/>\n"); 448 449 getSignTask().setJar(e); 450 getSignTask().setSignedjar(ext); 451 getSignTask().execute(); 452 } 453 } 454 } 455 456 private void processIndirectJars(Writer fileWriter, String dashcnb, String codebase) throws IOException , BuildException { 457 if (indirectJars == null) { 458 return; 459 } 460 DirectoryScanner scan = indirectJars.getDirectoryScanner(getProject()); 461 for (String f : scan.getIncludedFiles()) { 462 File jar = new File (scan.getBasedir(), f); 463 String rel = f.replace(File.separatorChar, '/'); 464 String sig = isSigned(jar); 465 String rel2 = rel.endsWith(".jar") ? rel : rel.replaceFirst("(\\.zip)?$", ".jar"); 467 File ext = new File (new File (target, dashcnb), rel2.replace('/', '-').replaceFirst("^modules-", "")); 468 Zip jartask = (Zip) getProject().createTask("jar"); 469 jartask.setDestFile(ext); 470 ZipFileSet zfs = new ZipFileSet(); 471 zfs.setSrc(jar); 472 if (sig != null) { 473 zfs.setExcludes("META-INF/" + sig + ".*"); 475 } 476 jartask.addZipfileset(zfs); 477 zfs = new ZipFileSet(); 478 File blank = File.createTempFile("empty", ""); 479 blank.deleteOnExit(); 480 zfs.setFile(blank); 481 zfs.setFullpath("META-INF/clusterpath/" + rel); 482 jartask.addZipfileset(zfs); 483 jartask.execute(); 484 blank.delete(); 485 fileWriter.write(" <jar HREF='" + dashcnb + '/' + ext.getName() + "'/>\n"); 486 getSignTask().setJar(ext); 487 getSignTask().setSignedjar(null); 488 getSignTask().execute(); 489 } 490 } 491 492 private static String relative(File file, File root) { 493 String sfile = file.toString().replace(File.separatorChar, '/'); 494 String sroot = (root.toString() + File.separator).replace(File.separatorChar, '/'); 495 if (sfile.startsWith(sroot)) { 496 return sfile.substring(sroot.length()); 497 } 498 return sfile; 499 } 500 501 502 private static String isSigned(File f) throws IOException { 503 JarFile jar = new JarFile (f); 504 try { 505 Enumeration <JarEntry > en = jar.entries(); 506 while (en.hasMoreElements()) { 507 Matcher m = SF.matcher(en.nextElement().getName()); 508 if (m.matches()) { 509 return m.group(1); 510 } 511 } 512 return null; 513 } finally { 514 jar.close(); 515 } 516 } 517 private static final Pattern SF = Pattern.compile("META-INF/(.+)\\.SF"); 518 519 } 520 | Popular Tags |