1 /* 2 * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.javafx.tools.packager; 27 28 import com.oracle.tools.packager.Bundlers; 29 import com.oracle.tools.packager.ConfigException; 30 import com.oracle.tools.packager.Log; 31 import com.oracle.tools.packager.Platform; 32 import com.oracle.tools.packager.UnsupportedPlatformException; 33 import com.sun.javafx.tools.packager.JarSignature.InputStreamSource; 34 import com.sun.javafx.tools.packager.bundlers.BundleParams; 35 import com.sun.javafx.tools.packager.bundlers.Bundler.BundleType; 36 import com.sun.javafx.tools.resource.PackagerResource; 37 import com.sun.javafx.css.parser.Css2Bin; 38 39 import java.io.BufferedReader; 40 import java.io.File; 41 import java.io.FileInputStream; 42 import java.io.FileNotFoundException; 43 import java.io.FileOutputStream; 44 import java.io.FileWriter; 45 import java.io.IOException; 46 import java.io.InputStream; 47 import java.io.InputStreamReader; 48 import java.io.OutputStream; 49 import java.io.Writer; 50 import java.nio.file.Files; 51 import java.nio.file.StandardCopyOption; 52 import java.security.InvalidKeyException; 53 import java.security.KeyStore; 54 import java.security.KeyStoreException; 55 import java.security.NoSuchAlgorithmException; 56 import java.security.PrivateKey; 57 import java.security.SignatureException; 58 import java.security.UnrecoverableKeyException; 59 import java.security.cert.Certificate; 60 import java.security.cert.CertificateException; 61 import java.security.cert.X509Certificate; 62 import java.text.MessageFormat; 63 import java.util.ArrayList; 64 import java.util.Base64; 65 import java.util.Enumeration; 66 import java.util.HashMap; 67 import java.util.HashSet; 68 import java.util.List; 69 import java.util.Map; 70 import java.util.Map.Entry; 71 import java.util.ResourceBundle; 72 import java.util.Set; 73 import java.util.jar.Attributes; 74 import java.util.jar.JarEntry; 75 import java.util.jar.JarFile; 76 import java.util.jar.JarOutputStream; 77 import java.util.jar.Manifest; 78 import java.util.zip.ZipEntry; 79 import java.util.zip.ZipOutputStream; 80 81 /** 82 * @deprecated use {@link ToolProvider} to locate the {@code "javapackager"} tool instead. 83 */ 84 @Deprecated(since="10", forRemoval=true) 85 public class PackagerLib { 86 public static final String JAVAFX_VERSION = System.getProperty("java.version"); 87 88 private static final ResourceBundle bundle = 89 ResourceBundle.getBundle("com/sun/javafx/tools/packager/Bundle"); 90 91 private CreateJarParams createJarParams; 92 private CreateBSSParams createBssParams; 93 private File bssTmpDir; 94 95 96 private enum Filter {ALL, CLASSES_ONLY, RESOURCES} 97 98 // if set of input resources consist of SINGLE element and 99 // this element is jar file then we expect this to be request to 100 // "update" jar file 101 // Input jar file MUST be executable jar file 102 // 103 // Check if we are in "special case" scenario 104 private File jarFileToUpdate(CreateJarParams params) { 105 if (params.resources.size() == 1) { 106 PackagerResource p = params.resources.get(0); 107 File f = p.getFile(); 108 if (!f.isFile() || !f.getAbsolutePath().toLowerCase().endsWith(".jar")) { 109 return null; 110 } 111 try (JarFile jf = new JarFile(f)) { 112 jf.getManifest(); //try to read manifest to validate it is jar 113 return f; 114 } catch (Exception e) { 115 //treat any exception as "not a special case" scenario 116 com.oracle.tools.packager.Log.verbose(e); 117 } 118 } 119 return null; 120 } 121 122 public void packageAsJar(CreateJarParams createJarParams) throws PackagerException { 123 if (createJarParams == null) { 124 throw new IllegalArgumentException("Parameters must not be null"); 125 } 126 127 if (createJarParams.outfile == null) { 128 throw new IllegalArgumentException("Output file is not specified"); 129 } 130 131 this.createJarParams = createJarParams; 132 133 //Special case: could be request for "update jar file" 134 File jarToUpdate = jarFileToUpdate(createJarParams); 135 Manifest m = null; 136 137 if (jarToUpdate != null) { 138 com.oracle.tools.packager.Log.info(MessageFormat.format(bundle.getString("MSG_UpdatingJar"), jarToUpdate.getAbsolutePath())); 139 try (JarFile jf = new JarFile(jarToUpdate)) { 140 //extract data we want to preserve 141 m = jf.getManifest(); 142 if (m != null) { 143 Attributes attrs = m.getMainAttributes(); 144 if (createJarParams.applicationClass == null) { 145 createJarParams.applicationClass = 146 attrs.getValue(Attributes.Name.MAIN_CLASS); 147 } 148 if (createJarParams.classpath == null) { 149 createJarParams.classpath = 150 attrs.getValue(Attributes.Name.CLASS_PATH); 151 } 152 if (createJarParams.codebase == null) { 153 createJarParams.codebase = 154 attrs.getValue(new Attributes.Name("Codebase")); 155 } 156 if (createJarParams.allPermissions == null) { 157 String value = 158 attrs.getValue(new Attributes.Name("Permissions")); 159 if (value != null) { 160 createJarParams.allPermissions = Boolean.valueOf(value); 161 } 162 } 163 } 164 } catch (IOException ex) { 165 throw new PackagerException( 166 ex, "ERR_FileReadFailed", jarToUpdate.getAbsolutePath()); 167 } 168 } 169 170 if (createJarParams.applicationClass == null) { 171 throw new IllegalArgumentException( 172 "Main application class is not specified"); 173 } 174 175 //NOTE: This should be a save-to-temp file, then rename operation 176 File applicationJar = new File(createJarParams.outdir, 177 createJarParams.outfile.endsWith(".jar") 178 ? createJarParams.outfile 179 : createJarParams.outfile + ".jar"); 180 181 if (jarToUpdate != null && 182 applicationJar.getAbsoluteFile().equals(jarToUpdate.getAbsoluteFile())) { 183 try { 184 File newInputJar = File.createTempFile("tempcopy", ".jar"); 185 Files.move(jarToUpdate.toPath(), newInputJar.toPath(), 186 StandardCopyOption.REPLACE_EXISTING); 187 jarToUpdate = newInputJar; 188 } catch (IOException ioe) { 189 throw new PackagerException( 190 ioe, "ERR_FileCopyFailed", jarToUpdate.getAbsolutePath()); 191 } 192 } 193 194 File parentFile = applicationJar.getParentFile(); 195 if (parentFile != null) { 196 parentFile.mkdirs(); 197 } 198 199 if (m == null) { 200 m = new Manifest(); 201 } 202 Attributes attr = m.getMainAttributes(); 203 attr.put(Attributes.Name.MANIFEST_VERSION, "1.0"); 204 attr.put(new Attributes.Name("Created-By"), "JavaFX Packager"); 205 206 if (createJarParams.manifestAttrs != null) { 207 for (Entry<String, String> e: createJarParams.manifestAttrs.entrySet()) { 208 attr.put(new Attributes.Name(e.getKey()), e.getValue()); 209 } 210 } 211 212 attr.put(Attributes.Name.MAIN_CLASS, createJarParams.applicationClass); 213 if (createJarParams.classpath != null) { 214 // Allow comma or semicolon as delimeter (turn them into spaces) 215 String cp = createJarParams.classpath; 216 cp = cp.replace(';', ' ').replace(',', ' '); 217 attr.put(Attributes.Name.CLASS_PATH, cp); 218 } 219 220 String existingSetting = attr.getValue("Permissions"); 221 if (existingSetting == null) { 222 attr.put(new Attributes.Name("Permissions"), 223 Boolean.TRUE.equals(createJarParams.allPermissions) ? "all-permissions" : "sandbox"); 224 } else if (createJarParams.allPermissions != null && !Boolean.valueOf(existingSetting).equals(createJarParams.allPermissions)) { 225 throw new PackagerException( 226 "ERR_ContradictorySetting", "Permissions"); 227 } 228 229 existingSetting = attr.getValue("Codebase"); 230 if (existingSetting == null) { 231 if (createJarParams.codebase != null) { 232 attr.put(new Attributes.Name("Codebase"), createJarParams.codebase); 233 } 234 } else if (createJarParams.codebase != null && !existingSetting.equals(createJarParams.codebase)) { 235 throw new PackagerException( 236 "ERR_ContradictorySetting", "Codebase"); 237 } 238 239 attr.put(new Attributes.Name("JavaFX-Version"), createJarParams.fxVersion); 240 241 if (createJarParams.preloader != null) { 242 attr.put(new Attributes.Name("JavaFX-Preloader-Class"), createJarParams.preloader); 243 } 244 245 246 if (createJarParams.arguments != null) { 247 int idx = 1; 248 for (String arg: createJarParams.arguments) { 249 attr.put(new Attributes.Name("JavaFX-Argument-"+idx), 250 encodeAsBase64(arg.getBytes())); 251 idx++; 252 } 253 } 254 if (createJarParams.params != null) { 255 int idx = 1; 256 for (Param p : createJarParams.params) { 257 if (p.name != null) { //otherwise it is something weird and we skip it 258 attr.put(new Attributes.Name("JavaFX-Parameter-Name-" + idx), 259 encodeAsBase64(p.name.getBytes())); 260 if (p.value != null) { //legal, means not value specified 261 attr.put(new Attributes.Name("JavaFX-Parameter-Value-" + idx), 262 encodeAsBase64(p.value.getBytes())); 263 } 264 idx++; 265 } 266 } 267 } 268 269 270 if (createJarParams.css2bin) { 271 try { 272 bssTmpDir = File.createTempFile("bssfiles", ""); 273 } catch (IOException ex) { 274 throw new PackagerException(ex, "ERR_CreatingTempFileFailed"); 275 } 276 bssTmpDir.delete(); 277 } 278 279 if (applicationJar.exists() && !applicationJar.delete()) { 280 throw new PackagerException( 281 "ERR_CantDeleteFile", createJarParams.outfile); 282 } 283 try { 284 jar(m, createJarParams.resources, jarToUpdate, 285 new JarOutputStream(new FileOutputStream(applicationJar)), 286 Filter.ALL); 287 } catch (IOException ex) { 288 throw new PackagerException( 289 ex, "ERR_CreatingJarFailed", createJarParams.outfile); 290 } 291 292 // cleanup 293 deleteDirectory(bssTmpDir); 294 this.createJarParams = null; 295 } 296 297 public void generateDeploymentPackages(DeployParams deployParams) throws PackagerException { 298 if (deployParams == null) { 299 throw new IllegalArgumentException("Parameters must not be null."); 300 } 301 302 try { 303 BundleParams bp = deployParams.getBundleParams(); 304 305 if (bp != null) { 306 switch(deployParams.getBundleType()) { 307 case NATIVE: { 308 // Generate disk images. 309 generateNativeBundles(deployParams.outdir, 310 bp.getBundleParamsAsMap(), 311 BundleType.IMAGE.toString(), 312 deployParams.getTargetFormat()); 313 314 // Generate installers. 315 generateNativeBundles(deployParams.outdir, 316 bp.getBundleParamsAsMap(), 317 BundleType.INSTALLER.toString(), 318 deployParams.getTargetFormat()); 319 break; 320 } 321 322 case JNLP: 323 case NONE: { 324 // Old school default. Just generate JNLP. 325 generateNativeBundles(deployParams.outdir, 326 bp.getBundleParamsAsMap(), 327 BundleType.JNLP.toString(), 328 "jnlp"); 329 break; 330 } 331 332 default: { 333 // A specefic output format, just generate that. 334 generateNativeBundles(deployParams.outdir, 335 bp.getBundleParamsAsMap(), 336 deployParams.getBundleType().toString(), 337 deployParams.getTargetFormat()); 338 } 339 } 340 } 341 } catch (PackagerException ex) { 342 throw ex; 343 } catch (Exception ex) { 344 throw new PackagerException(ex, "ERR_DeployFailed", ex.getMessage()); 345 } 346 347 } 348 349 private void generateNativeBundles(File outdir, Map<String, ? super Object> params, String bundleType, String bundleFormat) throws PackagerException { 350 for (com.oracle.tools.packager.Bundler bundler : Bundlers.createBundlersInstance().getBundlers(bundleType)) { 351 // if they specify the bundle format, require we match the ID 352 if (bundleFormat != null && !bundleFormat.equalsIgnoreCase(bundler.getID())) continue; 353 354 Map<String, ? super Object> localParams = new HashMap<>(params); 355 try { 356 if (bundler.validate(localParams)) { 357 File result = bundler.execute(localParams, outdir); 358 bundler.cleanup(localParams); 359 if (result == null) { 360 throw new PackagerException("MSG_BundlerFailed", bundler.getID(), bundler.getName()); 361 } 362 } 363 } catch (UnsupportedPlatformException e) { 364 com.oracle.tools.packager.Log.debug(MessageFormat.format(bundle.getString("MSG_BundlerPlatformException"), bundler.getName())); 365 } catch (ConfigException e) { 366 com.oracle.tools.packager.Log.debug(e); 367 if (e.getAdvice() != null) { 368 com.oracle.tools.packager.Log.info(MessageFormat.format(bundle.getString("MSG_BundlerConfigException"), bundler.getName(), e.getMessage(), e.getAdvice())); 369 } else { 370 com.oracle.tools.packager.Log.info(MessageFormat.format(bundle.getString("MSG_BundlerConfigExceptionNoAdvice"), bundler.getName(), e.getMessage())); 371 } 372 } catch (RuntimeException re) { 373 com.oracle.tools.packager.Log.info(MessageFormat.format(bundle.getString("MSG_BundlerRuntimeException"), bundler.getName(), re.toString())); 374 com.oracle.tools.packager.Log.debug(re); 375 } 376 } 377 } 378 379 public void generateBSS(CreateBSSParams params) throws PackagerException { 380 if (params == null) { 381 throw new IllegalArgumentException("Parameters must not be null."); 382 } 383 this.createBssParams = params; 384 createBinaryCss(createBssParams.resources, createBssParams.outdir); 385 this.createBssParams = null; 386 } 387 388 public void signJar(SignJarParams params) throws PackagerException { 389 try { 390 JarSignature signature = retrieveSignature(params); 391 392 for (PackagerResource pr: params.resources) { 393 signFile(pr, signature, params.outdir, params.verbose); 394 } 395 396 } catch (Exception ex) { 397 com.oracle.tools.packager.Log.verbose(ex); 398 throw new PackagerException("ERR_SignFailed", ex); 399 } 400 401 } 402 403 404 private JarSignature retrieveSignature(SignJarParams params) throws KeyStoreException, 405 NoSuchAlgorithmException, UnrecoverableKeyException, IOException, 406 CertificateException, InvalidKeyException { 407 if (params.keyPass == null) { 408 params.keyPass = params.storePass; 409 } 410 411 if (params.keyStore == null) { 412 throw new IOException("No keystore specified"); 413 } 414 415 if (params.storePass == null) { 416 throw new IOException("No store password specified"); 417 } 418 419 if (params.storeType == null) { 420 throw new IOException("No store type is specified"); 421 } 422 423 KeyStore store = KeyStore.getInstance(params.storeType); 424 store.load(new FileInputStream(params.keyStore), params.storePass.toCharArray()); 425 426 Certificate[] chain = store.getCertificateChain(params.alias); 427 X509Certificate certChain[] = new X509Certificate[chain.length]; 428 for (int i=0; i<chain.length; i++) { 429 certChain[i] = (X509Certificate) chain[i]; 430 } 431 432 PrivateKey privateKey = (PrivateKey) 433 store.getKey(params.alias, params.keyPass.toCharArray()); 434 435 return JarSignature.create(privateKey, certChain); 436 } 437 438 private void signFile( 439 PackagerResource pr, JarSignature signature, File outdir, boolean verbose) 440 throws NoSuchAlgorithmException, IOException, SignatureException { 441 if (pr.getFile().isDirectory()) { 442 File[] children = pr.getFile().listFiles(); 443 if (children != null) { 444 for (File innerFile : children) { 445 signFile(new PackagerResource( 446 pr.getBaseDir(), innerFile), signature, outdir, verbose); 447 } 448 } 449 } else { 450 File jar = pr.getFile(); 451 File parent = jar.getParentFile(); 452 String name = "bsigned_" + jar.getName(); 453 File signedJar = new File(parent, name); 454 455 Log.info("Signing (BLOB) " + jar.getPath()); 456 457 signAsBLOB(jar, signedJar, signature); 458 459 File destJar; 460 if (outdir != null) { 461 destJar = new File(outdir, pr.getRelativePath()); 462 } else { 463 // in-place 464 jar.delete(); 465 destJar = jar; 466 } 467 destJar.delete(); 468 destJar.getParentFile().mkdirs(); 469 signedJar.renameTo(destJar); 470 if (verbose) { 471 Log.info("Signed as " + destJar.getPath()); 472 } 473 } 474 } 475 476 private void signAsBLOB(final File jar, File signedJar, JarSignature signature) 477 throws IOException, NoSuchAlgorithmException, SignatureException 478 { 479 if (signature == null) { 480 throw new IllegalStateException("Should retrieve signature first"); 481 } 482 483 InputStreamSource in = () -> new FileInputStream(jar); 484 if (!signedJar.isFile()) { 485 signedJar.createNewFile(); 486 } 487 FileOutputStream fos = new FileOutputStream(signedJar); 488 signature.signJarAsBLOB(in, new ZipOutputStream(fos)); 489 } 490 491 public void makeAll(MakeAllParams makeAllParams) throws PackagerException { 492 final String exe = (Platform.getPlatform() == Platform.WINDOWS) ? ".exe" : ""; 493 String jHome = System.getenv("JAVA_HOME"); 494 if (jHome == null) { 495 jHome = System.getProperty("java.home"); 496 } 497 if (jHome == null) { 498 throw new PackagerException("ERR_MissingJavaHome"); 499 } 500 501 final File javac = new File(new File(jHome), "bin/javac" + exe); 502 503 final String srcDirName = "src"; 504 final String compiledDirName = "compiled"; 505 final String distDirName = "dist"; 506 final String outfileName = "dist"; 507 final String jarName = outfileName + ".jar"; 508 509 final File distDir = new File(distDirName); 510 511 final File compiledDir = new File(compiledDirName); 512 compiledDir.mkdir(); 513 514 try { 515 final File tmpFile = File.createTempFile("javac", "sources", new File(".")); 516 tmpFile.deleteOnExit(); 517 try (FileWriter sources = new FileWriter(tmpFile)) { 518 scanAndCopy(new PackagerResource(new File(srcDirName), "."), sources, compiledDir); 519 } 520 521 if (makeAllParams.verbose) { 522 Log.info("Executing javac:"); 523 Log.infof("%s %s %s %s %s %s%n", 524 javac.getAbsolutePath(), 525 "-d", compiledDirName, 526 "-cp", makeAllParams.classpath != null ? 527 makeAllParams.classpath : "<null>", 528 "@" + tmpFile.getAbsolutePath()); 529 } 530 531 int ret = -1; 532 if (makeAllParams.classpath != null) { 533 ret = execute( 534 javac.getAbsolutePath(), 535 "-d", compiledDirName, 536 "-cp", makeAllParams.classpath, 537 "@" + tmpFile.getAbsolutePath()); 538 } else { 539 ret = execute( 540 javac.getAbsolutePath(), 541 "-d", compiledDirName, 542 "@" + tmpFile.getAbsolutePath()); 543 } 544 545 if (ret != 0) { 546 throw new PackagerException("ERR_JavacFailed", Integer.toString(ret)); 547 } 548 } catch (PackagerException e) { 549 throw e; 550 } catch (Exception e) { 551 throw new PackagerException(e, "ERR_MakeAllJavacFailed"); 552 } 553 554 CreateJarParams cjp = new CreateJarParams(); 555 cjp.applicationClass = makeAllParams.appClass; 556 cjp.preloader = makeAllParams.preloader; 557 cjp.classpath = makeAllParams.classpath; 558 cjp.css2bin = false; 559 cjp.outdir = distDir; 560 cjp.outfile = jarName; 561 cjp.addResource(compiledDir, "."); 562 563 packageAsJar(cjp); 564 565 DeployParams dp = new DeployParams(); 566 dp.applicationClass = makeAllParams.appClass; 567 dp.appName = makeAllParams.appName; 568 dp.description = "Application description"; 569 dp.height = makeAllParams.height; 570 dp.width = makeAllParams.width; 571 dp.vendor = "Application vendor"; 572 dp.outdir = distDir; 573 dp.outfile = outfileName; 574 dp.addResource(distDir, jarName); 575 //noinspection deprecation 576 dp.setBundleType(BundleType.ALL); 577 578 generateDeploymentPackages(dp); 579 580 deleteDirectory(compiledDir); 581 } 582 583 @SuppressWarnings("unchecked") 584 private static int execute(Object ... args) throws IOException, InterruptedException { 585 final ArrayList<String> argsList = new ArrayList<>(); 586 for (Object a : args) { 587 if (a instanceof List) { 588 argsList.addAll((List)a); 589 } else if (a instanceof String) { 590 argsList.add((String)a); 591 } 592 } 593 final Process p = Runtime.getRuntime().exec(argsList.toArray(new String[argsList.size()])); 594 final BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream())); 595 Thread t = new Thread(() -> { 596 try { 597 String line; 598 while ((line = in.readLine()) != null) { 599 Log.info(line); 600 } 601 } catch (IOException ioe) { 602 com.oracle.tools.packager.Log.verbose(ioe); 603 } 604 }); 605 t.setDaemon(true); 606 t.start(); 607 final BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream())); 608 t = new Thread(() -> { 609 try { 610 String line; 611 while ((line = err.readLine()) != null) { 612 Log.error(line); 613 } 614 } catch (IOException ioe) { 615 Log.verbose(ioe); 616 } 617 }); 618 t.setDaemon(true); 619 t.start(); 620 return p.waitFor(); 621 } 622 623 private static void scanAndCopy(PackagerResource dir, Writer out, File outdir) throws PackagerException { 624 if (!dir.getFile().exists()) { 625 throw new PackagerException("ERR_MissingDirectory", dir.getFile().getName()); 626 } 627 File[] dirFilesList = dir.getFile().listFiles(); 628 if ((dirFilesList == null) || (dirFilesList.length == 0)) { 629 throw new PackagerException("ERR_EmptySourceDirectory", dir.getFile().getName()); 630 } 631 try { 632 for (File f : dirFilesList) { 633 if (f.isDirectory()) { 634 scanAndCopy(new PackagerResource(dir.getBaseDir(), f), out, outdir); 635 } else if (f.getName().endsWith(".java")) { 636 out.write('\'' + f.getAbsolutePath().replace('\\', '/') + "\'\n"); 637 } else { 638 copyFileToOutDir(new FileInputStream(f), 639 new File(outdir.getPath() + File.separator 640 + dir.getRelativePath() + File.separator 641 + f.getName())); 642 } 643 } 644 } catch (IOException ex) { 645 throw new PackagerException("ERR_FileCopyFailed", dir.getFile().getName()); 646 } 647 } 648 649 private String encodeAsBase64(byte inp[]) { 650 return Base64.getEncoder().encodeToString(inp); 651 } 652 653 private static void copyFileToOutDir( 654 InputStream isa, File fout) throws PackagerException { 655 656 final File outDir = fout.getParentFile(); 657 if (!outDir.exists() && !outDir.mkdirs()) { 658 throw new PackagerException("ERR_CreatingDirFailed", outDir.getPath()); 659 } 660 try (InputStream is = isa; OutputStream out = new FileOutputStream(fout)) { 661 byte[] buf = new byte[16384]; 662 int len; 663 while ((len = is.read(buf)) > 0) { 664 out.write(buf, 0, len); 665 } 666 } catch (IOException ex) { 667 throw new PackagerException(ex, "ERR_FileCopyFailed", outDir.getPath()); 668 } 669 } 670 671 private void jar( 672 Manifest manifest, List<PackagerResource> files, 673 File importJarFile, JarOutputStream jar, Filter filter) 674 throws IOException, PackagerException { 675 try { 676 jar.putNextEntry(new ZipEntry("META-INF/")); 677 jar.closeEntry(); 678 jar.putNextEntry(new ZipEntry(JarFile.MANIFEST_NAME)); 679 manifest.write(jar); 680 jar.closeEntry(); 681 682 alreadyAddedEntries.add("META-INF/"); 683 if (importJarFile != null) { //updating jar file 684 copyFromOtherJar(jar, importJarFile); 685 } else { //normal situation 686 for (PackagerResource pr : files) { 687 jar(pr.getFile(), jar, filter, 688 pr.getBaseDir().getAbsolutePath().length() + 1); 689 } 690 } 691 } finally { 692 jar.close(); 693 alreadyAddedEntries.clear(); 694 } 695 } 696 697 private Set<String> alreadyAddedEntries = new HashSet<>(); 698 private void createParentEntries(String relativePath, JarOutputStream jar) throws IOException { 699 String[] pathComponents = relativePath.split("/"); 700 StringBuilder pathSB = new StringBuilder(); 701 // iterating over directories only, the last component is the file 702 // or will be created next time. 703 for (int i = 0; i < pathComponents.length - 1; i++) { 704 pathSB.append(pathComponents[i]).append("/"); 705 if (!alreadyAddedEntries.contains(pathSB.toString())) { 706 jar.putNextEntry(new ZipEntry(pathSB.toString())); 707 jar.closeEntry(); 708 } 709 alreadyAddedEntries.add(pathSB.toString()); 710 } 711 } 712 713 //add everything but manifest from given jar file 714 private void copyFromOtherJar(JarOutputStream jar, File inputFile) throws IOException { 715 JarFile inJar = new JarFile(inputFile); 716 717 Enumeration<JarEntry> all = inJar.entries(); 718 while (all.hasMoreElements()) { 719 JarEntry je = all.nextElement(); 720 721 //skip manifest or root manifest dir entry (can not add duplicate) 722 if ("META-INF/MANIFEST.MF".equals(je.getName().toUpperCase()) 723 || "META-INF/".equals(je.getName().toUpperCase())) { 724 continue; 725 } 726 727 jar.putNextEntry(new JarEntry(je.getName())); 728 729 byte b[] = new byte[65000]; 730 int i; 731 try (InputStream in = inJar.getInputStream(je)) { 732 while ((i = in.read(b)) > 0) { 733 jar.write(b, 0, i); 734 } 735 } 736 737 jar.closeEntry(); 738 } 739 } 740 741 private void jar(File f, JarOutputStream jar, Filter filter, int cut) 742 throws IOException, PackagerException { 743 if (!f.exists()) { 744 throw new FileNotFoundException("Input folder does not exist [" 745 +f.getAbsolutePath()+"]"); 746 } 747 748 if (f.isDirectory()) { 749 File[] children = f.listFiles(); 750 if (children != null) { 751 for (File innerFile : children) { 752 jar(innerFile, jar, filter, cut); 753 } 754 } 755 } else if (filter == Filter.ALL 756 || (filter == Filter.CLASSES_ONLY && f.getName().endsWith(".class")) 757 || (filter == Filter.RESOURCES && isResource(f.getAbsolutePath()))) { 758 final String absPath = f.getAbsolutePath(); 759 if (absPath.endsWith("META-INF\\MANIFEST.MF") 760 || absPath.endsWith("META-INF/MANIFEST.MF")) { 761 return; 762 } 763 createParentEntries(absPath.substring(cut).replace('\\', '/'), jar); 764 if (createJarParams.css2bin && f.getName().endsWith(".css")) { 765 // generate bss file into temporary directory 766 int startOfExt = absPath.lastIndexOf(".") + 1; 767 String bssFileName = absPath 768 .substring(cut, startOfExt) 769 .concat("bss"); 770 771 File bssFile = new File(bssTmpDir, bssFileName); 772 bssFile.getParentFile().mkdirs(); 773 774 createBinaryCss(absPath, bssFile.getAbsolutePath()); 775 jar.putNextEntry(new ZipEntry(bssFileName.replace('\\', '/'))); 776 f = bssFile; 777 } else { 778 jar.putNextEntry(new ZipEntry(absPath.substring(cut).replace('\\', '/'))); 779 } 780 781 byte b[] = new byte[65000]; 782 int i; 783 784 try (FileInputStream in = new FileInputStream(f)) { 785 while ((i = in.read(b)) > 0) { 786 jar.write(b, 0, i); 787 } 788 } 789 jar.closeEntry(); 790 } 791 } 792 793 private void createBinaryCss(List<PackagerResource> cssResources, File outdir) 794 throws PackagerException { 795 for (PackagerResource cssRes: cssResources) { 796 String relPath = cssRes.getRelativePath(); 797 createBinaryCss(cssRes.getFile(), outdir, relPath); 798 } 799 } 800 801 private void createBinaryCss(File f, File outdir, String relPath) 802 throws PackagerException { 803 if (f.isDirectory()) { 804 File[] children = f.listFiles(); 805 if (children != null) { 806 for (File innerFile : children) { 807 createBinaryCss(innerFile, outdir, relPath + '/' + innerFile.getName()); 808 } 809 } 810 } else if (f.getName().endsWith(".css")) { 811 String cssFileName = f.getAbsolutePath(); 812 String bssFileName = new File(outdir.getAbsolutePath(), 813 replaceExtensionByBSS(relPath)) 814 .getAbsolutePath(); 815 createBinaryCss(cssFileName, bssFileName); 816 } 817 } 818 819 private void createBinaryCss(String cssFile, String binCssFile) throws PackagerException { 820 String ofname = (binCssFile != null) 821 ? binCssFile 822 : replaceExtensionByBSS(cssFile); 823 824 // create parent directories 825 File of = new File(ofname); 826 File parentFile = of.getParentFile(); 827 if (parentFile != null) { 828 parentFile.mkdirs(); 829 } 830 831 try { 832 Css2Bin.convertToBinary(cssFile, ofname); 833 } catch (IOException ex) { 834 Throwable causeEx = ex.getCause(); 835 String cause = (causeEx != null) ? causeEx.getMessage() 836 : bundle.getString("ERR_UnknownReason"); 837 838 throw new PackagerException(ex, "ERR_BSSConversionFailed", cssFile, cause); 839 } 840 } 841 842 private static String replaceExtensionByBSS(String cssName) { 843 return cssName.substring(0, cssName.lastIndexOf(".") + 1).concat("bss"); 844 } 845 846 847 private boolean isResource(String name) { 848 if (name.endsWith(".class")) { 849 return false; 850 } 851 if (name.endsWith(".java")) { 852 return false; 853 } 854 if (name.endsWith(".fx")) { 855 return false; 856 } 857 if (name.endsWith(".cvsignore")) { 858 return false; 859 } 860 if (name.endsWith(".hgignore")) { 861 return false; 862 } 863 if (name.endsWith("vssver.scc")) { 864 return false; 865 } 866 if (name.endsWith(".DS_Store")) { 867 return false; 868 } 869 if (name.endsWith("~")) { 870 return false; 871 } 872 name = name.replace('\\', '/'); 873 if (name.contains("/CVS/")) { 874 return false; 875 } 876 if (name.contains("/.svn/")) { 877 return false; 878 } 879 if (name.contains("/.hg/")) { 880 return false; 881 } 882 if (name.contains("/.#")) { 883 return false; 884 } 885 if (name.contains("/._")) { 886 return false; 887 } 888 if (name.endsWith("#") && name.contains("/#")) { 889 return false; 890 } 891 if (name.endsWith("%") && name.contains("/%")) { 892 return false; 893 } 894 if (name.endsWith("MANIFEST.MF")) { 895 return false; 896 } 897 return true; 898 } 899 900 private static boolean deleteDirectory(File dir) { 901 if (dir == null || !dir.exists()) { 902 return false; 903 } 904 905 if (dir.isDirectory()) { 906 for (String file : dir.list()) { 907 deleteDirectory(new File(dir, file)); 908 } 909 } 910 return dir.delete(); 911 } 912 }