0001: package tide.export;
0002:
0003: import java.awt.EventQueue;
0004: import tide.project.*;
0005: import tide.editor.*;
0006: import tide.sources.SourceFile;
0007: import tide.compiler.*;
0008: import tide.exttools.proguard.ProGuardSettingsDialog;
0009: import tide.exttools.proguard.ProGuardLauncher;
0010: import javax.swing.*;
0011: import java.awt.Component;
0012: import java.awt.FlowLayout;
0013: import java.awt.BorderLayout;
0014: import java.awt.Insets;
0015: import java.awt.event.*;
0016: import javax.swing.border.*;
0017: import java.util.*;
0018: import java.io.*;
0019: import java.net.*;
0020: import snow.utils.*;
0021: import snow.utils.storage.*;
0022: import snow.utils.gui.*;
0023: import snow.concurrent.*;
0024:
0025: /** Todo: classpath check if indexing or jnlp.
0026: * Cool: one click to create,sign pack and publish. example:
0027: * javaws http://snowmail.sn.funpic.de/tide/tide.jnlp
0028: *
0029: * [July2006] found in the JDK guides:
0030: * Pack200 rearranges the contents of the resultant JAR file. The jarsigner hashes the contents of the class file
0031: * and stores the hash in an encrypted digest in the manifest. When the unpacker runs on a packed packed, the contents
0032: * of the classes will be rearranged and thus invalidate the signature. Therefore, the JAR file must be normalized first
0033: * using pack200 and unpack200, and thereafter signed.
0034: *
0035: * (Here's why this works: Any reordering the packer does of any classfile structures is idempotent, so the second packing
0036: * does not change the orderings produced by the first packing. Also, the unpacker is guaranteed by the JSR 200 specification
0037: * to produce a specific bytewise image for any given transmission ordering of archive elements.)
0038: *
0039: * [Feb2008] added quiet mode.
0040: */
0041: public class JarCreationDialog extends JDialog {
0042: private final JToggleButton advancedModeBT = new JToggleButton(
0043: "Advanced mode >>");
0044: private List<Component> advancedItems = new ArrayList<Component>();
0045:
0046: private final FileField destination = new FileField("", true,
0047: "Choose the jar destination", JFileChooser.FILES_ONLY);
0048: private final JCheckBox createIndex = new JCheckBox(
0049: "Copy classpath libs to dest", false);
0050: private final JCheckBox createBatch = new JCheckBox(
0051: "Create batch launcher", false);
0052: private final JCheckBox signArchive = new JCheckBox("Sign", false);
0053: // TODO! problem: some jars like the mail API mail.jar have special infos in the manifest...
0054: // => should be merged !
0055: //private final JCheckBox mergeClassPath = new JCheckBox("Copy classpath jars", false);
0056: private final JCheckBox createJNLP = new JCheckBox(
0057: "Create JNLP launcher", false);
0058: private final JCheckBox includeSources = new JCheckBox(
0059: "Include sources", false);
0060: private final JCheckBox includeResources = new JCheckBox(
0061: "Include resources (all files but sources)", true);
0062: private final JCheckBox createPack200 = new JCheckBox(
0063: "Create pack200 archive", false);
0064: private final JCheckBox publish = new JCheckBox("Publish", false);
0065: private final JCheckBox quietMode = new JCheckBox("Quiet mode",
0066: false);
0067:
0068: private final FileField srcDestination = new FileField("", true,
0069: "Choose the sources zip destination",
0070: JFileChooser.FILES_ONLY);
0071: private final JCheckBox createSourceArchiveCB = new JCheckBox(
0072: "Create a sources zip", false);
0073: private final JCheckBox includeResourcesInSrc = new JCheckBox(
0074: "Include resources", false);
0075:
0076: private final JCheckBox proguardCB = new JCheckBox(
0077: "Apply Proguard", false);
0078:
0079: private final JarSignerPanel jarSignerPanel;
0080: private final JNLPPanel jNLPPanel;
0081:
0082: private final JCheckBox useCustomManifest = new JCheckBox(
0083: "Use custom manifest", false);
0084: private final FileField customManifestPath = new FileField("",
0085: true, "Choose the custom manifest file",
0086: JFileChooser.FILES_ONLY);
0087:
0088: // the reference is changed during progress to make the dialog no more modal (after class add)!
0089: private ProgressModalDialog pmd = null;
0090:
0091: public JarCreationDialog(final JFrame parent,
0092: final ProjectSettings settings) {
0093: super (parent, "" + settings.getProjectName()
0094: + " jar file creation", true);
0095:
0096: jarSignerPanel = new JarSignerPanel(this );
0097: jNLPPanel = new JNLPPanel();
0098:
0099: JPanel controlPanelNorth = new JPanel(new FlowLayout(
0100: FlowLayout.LEFT, 10, 5));
0101: add(controlPanelNorth, BorderLayout.NORTH);
0102: controlPanelNorth.add(advancedModeBT);
0103: advancedModeBT.setFocusPainted(false);
0104: advancedModeBT.setMargin(new Insets(0, 1, 0, 1));
0105: advancedModeBT.setSelected(settings.getBooleanProperty(
0106: "JAR_advancedModeBT", false));
0107: advancedModeBT.addActionListener(new ActionListener() {
0108: public void actionPerformed(ActionEvent ae) {
0109: updateAdvancedOrSimpleMode();
0110: }
0111: });
0112:
0113: JPanel inputPanel = new JPanel();
0114: inputPanel.setBorder(new EmptyBorder(5, 2, 2, 2));
0115: add(inputPanel, BorderLayout.CENTER);
0116: GridLayout3 gl3 = new GridLayout3(2, inputPanel);
0117: gl3.add("Jar destination");
0118: gl3.add(destination);
0119:
0120: File dest = new File(settings.getProperty("JAR_DESTINATION",
0121: settings.getSources_Home() + "/../pub/"
0122: + settings.getProjectName() + ".jar"));
0123: try // remove the .. in the relative parts of the path
0124: {
0125: dest = dest.getCanonicalFile();
0126: } catch (Exception ignored) {
0127: ignored.printStackTrace();
0128: }
0129: destination.setPath(dest.getAbsolutePath());
0130: destination.offerRememberedGlobalCompletion(
0131: settings.getProps(), "JAR_knownDestinations");
0132: destination.setComponentWidth(340);
0133: destination.setAutoColorized();
0134:
0135: gl3.add(includeSources);
0136: includeSources.setSelected(settings.getBooleanProperty(
0137: "JAR_includeSources", true));
0138:
0139: JPanel respan = new JPanel(
0140: new FlowLayout(FlowLayout.LEFT, 0, 0));
0141: gl3.add(respan);
0142: respan.add(includeResources);
0143: includeResources.setSelected(settings.getBooleanProperty(
0144: "JAR_includeResources", true));
0145: final JButton resEx = new JButton("except");
0146: resEx.setMargin(new Insets(0, 0, 0, 0));
0147: resEx.setFocusPainted(false);
0148: respan.add(resEx);
0149:
0150: includeResources.addActionListener(new ActionListener() {
0151: public void actionPerformed(ActionEvent ae) {
0152: resEx.setVisible(includeResources.isSelected());
0153: }
0154: });
0155: resEx.setVisible(includeResources.isSelected());
0156:
0157: resEx.addActionListener(new ActionListener() {
0158: public void actionPerformed(ActionEvent ae) {
0159: String rep = JOptionPane
0160: .showInputDialog(
0161: JarCreationDialog.this ,
0162: "Enter the list of filename extensions to ignore, separated with spaces",
0163: settings.getProperty(
0164: "resources_ign_ext",
0165: ".tmp .temp"));
0166: if (rep != null) {
0167: settings.setProperty("resources_ign_ext", rep);
0168: }
0169: }
0170: });
0171:
0172: advancedItems.add(gl3.add(useCustomManifest));
0173: advancedItems.add(gl3.add(customManifestPath));
0174: customManifestPath.setPath(settings.getProperty(
0175: "JAR_CUSTOM_MANIFESTPATH", settings
0176: .getRootSiblingFolder("dev")
0177: + "/dev/custom_manifest.mf"));
0178: customManifestPath.setAutoColorized();
0179: this .useCustomManifest.setSelected(settings.getBooleanProperty(
0180: "JAR_useCustomManifest", false));
0181: useCustomManifest.addActionListener(new ActionListener() {
0182: public void actionPerformed(ActionEvent ae) {
0183: customManifestPath.setEditable(useCustomManifest
0184: .isSelected());
0185: customManifestPath.makeVisible(useCustomManifest
0186: .isSelected());
0187: }
0188: });
0189: customManifestPath.setComponentWidth(340);
0190: customManifestPath.setEditable(useCustomManifest.isSelected());
0191: customManifestPath.makeVisible(useCustomManifest.isSelected());
0192:
0193: // gl3.add(mergeClassPath);
0194: // gl3.add("(copy all the included jars in the destination jar)");
0195: // mergeClassPath.setSelected( settings.getBooleanProperty("JAR_mergeClassPath", true) );
0196:
0197: gl3.add(signArchive);
0198: signArchive.setSelected(settings.getBooleanProperty(
0199: "JAR_signArchive", false));
0200: signArchive.addActionListener(new ActionListener() {
0201: public void actionPerformed(ActionEvent ae) {
0202: jarSignerPanel.setEditEnabled(signArchive.isSelected());
0203: jarSignerPanel.makeVisible(signArchive.isSelected());
0204: pack();
0205: }
0206: });
0207: gl3.add(jarSignerPanel);
0208: jarSignerPanel.readValuesFromIniFile();
0209:
0210: // sources archive
0211: advancedItems.add(gl3
0212: .addTitleSeparator("Separate archive for sources"));
0213: advancedItems.add(gl3.add(createSourceArchiveCB));
0214: createSourceArchiveCB.setSelected(settings.getBooleanProperty(
0215: "JAR_createSourceArchive", false));
0216: final JPanel sepSrcPanel = new JPanel();
0217: sepSrcPanel.setLayout(new BoxLayout(sepSrcPanel,
0218: BoxLayout.Y_AXIS));
0219: final JPanel wp2 = new JPanel(new FlowLayout(FlowLayout.LEFT,
0220: 0, 0));
0221: wp2.add(sepSrcPanel);
0222: advancedItems.add(gl3.add(wp2));
0223: sepSrcPanel.add(GUIUtils.wrapLeft(srcDestination, 0));
0224: sepSrcPanel.add(GUIUtils.wrapLeft(includeResourcesInSrc, 0));
0225: includeResourcesInSrc.setSelected(settings.getBooleanProperty(
0226: "JAR_includeResourcesInSrc", false));
0227:
0228: srcDestination.setComponentWidth(340);
0229: File sdest = FileUtils.getCanonicalFileWithCase(new File(
0230: settings.getProperty("JAR_SRC_DESTINATION", settings
0231: .getSources_Home()
0232: + "/../pub/"
0233: + settings.getProjectName()
0234: + "_src.zip")));
0235: srcDestination.setPath(sdest.getAbsolutePath());
0236: srcDestination.setAutoColorized();
0237: createSourceArchiveCB.addActionListener(new ActionListener() {
0238: public void actionPerformed(ActionEvent ae) {
0239: sepSrcPanel.setVisible(createSourceArchiveCB
0240: .isSelected());
0241: pack();
0242: }
0243: });
0244: sepSrcPanel.setVisible(createSourceArchiveCB.isSelected());
0245:
0246: if (ProGuardSettingsDialog.isConfigured()) {
0247: advancedItems.add(gl3.addTitleSeparator("Proguard"));
0248: advancedItems.add(gl3.add(proguardCB));
0249: proguardCB.setSelected(settings.getBooleanProperty(
0250: "JAR_enableProguard", false));
0251:
0252: advancedItems.add(gl3
0253: .addTF("shrink and/or optimize and/or obfuscate"));
0254: }
0255:
0256: advancedItems.add(gl3.addTitleSeparator("Options"));
0257:
0258: advancedItems.add(gl3.add(createBatch));
0259: advancedItems
0260: .add(gl3
0261: .addTF("create a batch launcher with the app and JVM arguments"));
0262: //no, let it select manually
0263: // createBatch.setSelected( settings.getBooleanProperty("JAR_createBatch", true) );
0264:
0265: // for indexing, the target directory must contain ext the libraries that are referenced in the classpath
0266:
0267: // only make sense if classpath is not empty
0268: List<File> cp = settings.getClassPath(false, true); // only the user (aux) classpath
0269: if (cp.size() > 0) {
0270: advancedItems.add(gl3.add(createIndex));
0271: advancedItems
0272: .add(gl3
0273: .addTF("copy the ext jars to dest, and sign and index them"));
0274: }
0275: //no, let it only be manually selected, requires that the libs in the classpath are present
0276: // at destination path...
0277: //createIndex.setSelected( settings.getBooleanProperty("JAR_createIndex", false) );
0278:
0279: advancedItems.add(gl3.add(this .createJNLP));
0280: advancedItems.add(gl3.addTF("write a jnlp launcher file"));
0281: // no, let it only be manually selected, make sense to generate only once...
0282: //createJNLP.setSelected( settings.getBooleanProperty("JAR_createJNLP", false) );
0283:
0284: advancedItems.add(gl3.add(createPack200));
0285: advancedItems
0286: .add(gl3
0287: .addTF("create a .pack200.gz compressed copy of the jar"));
0288: createPack200.setSelected(settings.getBooleanProperty(
0289: "JAR_createPack200", false));
0290:
0291: // publish
0292: //
0293: JPanel publishTypePanel = new JPanel(new FlowLayout(
0294: FlowLayout.LEFT, 0, 0));
0295: publishTypePanel.add(publish);
0296: advancedItems.add(gl3.add(publishTypePanel));
0297: //
0298: publish.setSelected(settings.getBooleanProperty("JAR_publish",
0299: false));
0300:
0301: advancedItems
0302: .add(gl3
0303: .addTF("upload the generated files to an ftp fileserver or any other folder"));
0304:
0305: advancedItems.add(gl3.add(this .quietMode));
0306: advancedItems
0307: .add(gl3
0308: .addTF("Quiet mode, compile all and overwrite existing jar without confirm"));
0309: quietMode.setSelected(settings.getBooleanProperty(
0310: "JAR_quietMode", false));
0311:
0312: CloseControlPanel ccp = new CloseControlPanel(this , true, true,
0313: "Create Jar");
0314: ccp.getOkButton().setIcon(Icons.sharedRightArrow);
0315: add(ccp, BorderLayout.SOUTH);
0316:
0317: pack();
0318:
0319: jarSignerPanel.setEditEnabled(signArchive.isSelected());
0320: jarSignerPanel.makeVisible(signArchive.isSelected());
0321:
0322: updateAdvancedOrSimpleMode();
0323:
0324: this .setLocationRelativeTo(parent);
0325: this .setVisible(true); // MODAL => waits
0326:
0327: if (ccp.getWasCancelled())
0328: return; // cancel was pressed
0329: if (!ccp.getWasAccepted())
0330: return; // close was pressed
0331:
0332: if (createJNLP.isSelected() && advancedModeBT.isSelected()) // not in simple mode
0333: {
0334: JDialog d = new JDialog(parent, "JNLP launcher parameters",
0335: true);
0336: d.add(this .jNLPPanel, BorderLayout.CENTER);
0337: CloseControlPanel ccpd = new CloseControlPanel(d, true,
0338: true, "Ok");
0339: d.add(ccpd, BorderLayout.SOUTH);
0340: d.pack();
0341: d.setLocationRelativeTo(parent);
0342: d.setVisible(true); // Modal => waits
0343:
0344: if (ccpd.getWasCancelled()) {
0345: createJNLP.setSelected(false);
0346: } else {
0347: jNLPPanel.saveSettingsInInifile();
0348: }
0349: }
0350:
0351: // save settings
0352: jarSignerPanel.saveSettingsInInifile();
0353:
0354: settings.setBooleanProperty("JAR_advancedModeBT",
0355: advancedModeBT.isSelected());
0356:
0357: settings.setProperty("JAR_DESTINATION", destination.getPath()
0358: .getAbsolutePath());
0359: destination.rememberPathForGlobalCompletion(
0360: settings.getProps(), "JAR_knownDestinations");
0361:
0362: settings.setBooleanProperty("JAR_createIndex", createIndex
0363: .isSelected());
0364: settings.setBooleanProperty("JAR_includeSources",
0365: includeSources.isSelected());
0366: settings.setBooleanProperty("JAR_includeResources",
0367: includeResources.isSelected());
0368: settings.setBooleanProperty("JAR_signArchive", signArchive
0369: .isSelected());
0370: settings.setBooleanProperty("JAR_createJNLP", createJNLP
0371: .isSelected());
0372: settings.setBooleanProperty("JAR_createBatch", createBatch
0373: .isSelected());
0374: // settings.setBooleanProperty("JAR_mergeClassPath", mergeClassPath.isSelected());
0375: settings.setBooleanProperty("JAR_createPack200", createPack200
0376: .isSelected());
0377: settings.setBooleanProperty("JAR_useCustomManifest",
0378: this .useCustomManifest.isSelected());
0379:
0380: if (customManifestPath.getPath() != null) {
0381: settings.setProperty("JAR_CUSTOM_MANIFESTPATH", ""
0382: + customManifestPath.getPath());
0383: }
0384:
0385: if (srcDestination.getPath() != null) {
0386: settings.setProperty("JAR_SRC_DESTINATION", ""
0387: + srcDestination.getPath());
0388: }
0389: settings.setBooleanProperty("JAR_createSourceArchive",
0390: createSourceArchiveCB.isSelected());
0391: settings.setBooleanProperty("JAR_includeResourcesInSrc",
0392: includeResourcesInSrc.isSelected());
0393:
0394: settings.setBooleanProperty("JAR_publish", this .publish
0395: .isSelected());
0396: settings.setBooleanProperty("JAR_quietMode", quietMode
0397: .isSelected());
0398:
0399: settings.setBooleanProperty("JAR_enableProguard",
0400: this .proguardCB.isSelected());
0401:
0402: // prepare the destination
0403: //
0404:
0405: // may be null
0406: final String mainClassName = settings.getMainClassJavaName();
0407: final File fdest = new File(settings.getProperty(
0408: "JAR_DESTINATION", null));
0409:
0410: if (fdest.exists()) {
0411: if (fdest.isDirectory()) {
0412: JOptionPane
0413: .showMessageDialog(
0414: null,
0415: "bad destination\n\n "
0416: + fdest
0417: + "\n\nThe destination must be a file, not a folder.",
0418: "Error", JOptionPane.ERROR_MESSAGE);
0419: return;
0420: }
0421:
0422: if (!quietMode.isSelected()) {
0423: int rep = JOptionPane
0424: .showConfirmDialog(
0425: pmd,
0426: "The jar file already exists. Do you want to overwrite it ?",
0427: "Confirm jar file overwrite",
0428: JOptionPane.YES_NO_OPTION);
0429: if (rep != JOptionPane.YES_OPTION)
0430: return;
0431: }
0432:
0433: if (!fdest.delete()) {
0434: int rep = JOptionPane
0435: .showConfirmDialog(
0436: pmd,
0437: "Cannot delete the file "
0438: + fdest
0439: + "\nIt may be in use in a JVM, close the application manually and continue or cancel."
0440: + "\nDo you want to continue ?",
0441: "Destination file is currently not deletable",
0442: JOptionPane.YES_NO_OPTION);
0443: if (rep != JOptionPane.YES_OPTION)
0444: return;
0445:
0446: if (!fdest.delete()) {
0447: JOptionPane
0448: .showMessageDialog(
0449: parent,
0450: "Cannot delete the file "
0451: + fdest
0452: + "\nIt may be in use in another JVM."
0453: + "\nClose the other java applications and try again."
0454: + "\nHints: use ps -l and kill under linux, sysinternals process explorer in Windows).",
0455: "Jar archive creation failed",
0456: JOptionPane.ERROR_MESSAGE);
0457: return;
0458:
0459: }
0460: }
0461: }
0462:
0463: // signature verification
0464: if (signArchive.isSelected()) {
0465: String ok = this .jarSignerPanel.isKeystoreAndPasswordOK();
0466: if (ok != null) {
0467: JOptionPane
0468: .showMessageDialog(
0469: null,
0470: "Cannot create the jar file, the signature configuration is erroneous or incomplete:\n\n"
0471: + " Error = " + ok,
0472: "Bad signature configuration",
0473: JOptionPane.ERROR_MESSAGE);
0474: return;
0475: }
0476: }
0477:
0478: // The jar creation
0479: //
0480:
0481: pmd = new ProgressModalDialog(parent, "Jar creation", true); // not modal during the whole process
0482: pmd.start();
0483: Runnable ru = new Runnable() {
0484: public void run() {
0485: try {
0486: createJarFile(fdest);
0487: } catch (final Exception e) {
0488: EventQueue.invokeLater(new Runnable() {
0489: public void run() {
0490: JOptionPane.showMessageDialog(
0491: null, // pmd may be null or terminated !!
0492: "Exception occured during Jar creation:\n"
0493: + e.getMessage(),
0494: "Jar archive creation failed",
0495: JOptionPane.ERROR_MESSAGE);
0496: }
0497: });
0498: e.printStackTrace();
0499: } finally {
0500: pmd.closeDialog();
0501: }
0502: }
0503: };
0504: Thread t = new Thread(ru);
0505: t.setPriority(Thread.NORM_PRIORITY - 1);
0506: t.start();
0507: } // Constructor
0508:
0509: private void updateAdvancedOrSimpleMode() {
0510: for (Component c : advancedItems) {
0511: c.setVisible(advancedModeBT.isSelected());
0512: }
0513:
0514: if (advancedModeBT.isSelected()) {
0515: advancedModeBT.setText("Simple mode >>");
0516: } else {
0517: advancedModeBT.setText("Advanced mode >>");
0518: }
0519:
0520: // tricky, but needed (fix it !)
0521:
0522: boolean is = createSourceArchiveCB.isSelected();
0523: createSourceArchiveCB.setSelected(!is);
0524: createSourceArchiveCB.doClick();
0525:
0526: pack();
0527: }
0528:
0529: /** Checks if the class path libs are present at destination.
0530: * ask and copy them if wanted.
0531: * also offer to sign them.
0532: *
0533: * this should be called when indexing and/or gnerating jnlp.
0534: */
0535: private void checkClassPathPresenceAtDestination(File destDir,
0536: ProgressModalDialog pmd) {
0537: final ProjectSettings actualProject = MainEditorFrame.instance
0538: .getActualProject();
0539: List<File> cp = actualProject.getClassPath(false, true); // only the user (aux) classpath
0540:
0541: List<File> notPresent = new ArrayList<File>();
0542: for (File lf : cp) {
0543: File destLib = new File(destDir, lf.getName());
0544: if (!destLib.exists()) {
0545: notPresent.add(lf);
0546: }
0547: }
0548:
0549: if (notPresent.size() > 0) {
0550: StringBuilder mess = new StringBuilder(
0551: "The following external libraries required in your project:\r\n");
0552: for (File f : notPresent) {
0553: mess.append("\r\n " + f.getAbsolutePath());
0554: }
0555: mess
0556: .append("\n\nare missing in the destination "
0557: + destDir);
0558: mess
0559: .append("\nThey must be present there when indexing the jar file or when creating a jnlp launcher.");
0560: mess
0561: .append("\n\nShould tIDE copy these libraries to the destination for you ?");
0562:
0563: int rep = JOptionPane.showConfirmDialog(pmd, "" + mess,
0564: "Libraries Not Present At Destination",
0565: JOptionPane.YES_NO_OPTION);
0566: if (rep != JOptionPane.YES_OPTION)
0567: return;
0568:
0569: // copy
0570: for (File lf : notPresent) {
0571: File destLib = new File(destDir, lf.getName());
0572: pmd.setCommentLabel("Copying ext lib to " + destLib);
0573: try {
0574: FileUtils.writeToFile(new FileInputStream(lf),
0575: destLib);
0576: } catch (Exception ex) {
0577: JOptionPane.showMessageDialog(this ,
0578: "Cannot copy\n " + lf + "\nto\n "
0579: + destLib + "\n\nError:\n "
0580: + ex.getMessage(),
0581: "Cannot copy external lib",
0582: JOptionPane.ERROR_MESSAGE);
0583: ex.printStackTrace();
0584: }
0585: }
0586:
0587: if (signArchive.isSelected()) {
0588: // for all the copied libs (all from class path)
0589: ll: for (File lf : cp) {
0590: File destLib = new File(destDir, lf.getName());
0591: MainEditorFrame.debugOut("Analyse signature for "
0592: + destLib);
0593: if (!destLib.exists())
0594: continue ll; // may not have been copied
0595:
0596: // look if the libs are already signed
0597: try {
0598: List<String> signs = JarSigner
0599: .readAllExistingSignatures(destLib);
0600: boolean deleteExistingSignatures = false;
0601:
0602: // only let it pass if it was already signed with a key with the same alias (TODO: compare fingerprints ! ...)
0603: if (signs.size() == 1
0604: && signs.get(0).equals(
0605: jarSignerPanel
0606: .getKeyStoreAlias()
0607: .toLowerCase())) {
0608: rep = JOptionPane
0609: .showConfirmDialog(
0610: pmd,
0611: "The external library "
0612: + destLib.getName()
0613: + "\nIs already signed with the alias "
0614: + jarSignerPanel
0615: .getKeyStoreAlias()
0616: + "."
0617: + "\nIt must correspond to the be the same key as the one used to sign the project jar file."
0618: + "\nOtherwise, WebStart will not be able to launch the application."
0619: + "\nIf you're not sure, it is recommanded to let tIDE delete this signature and resign it for you."
0620: + "\nShould tIDE remove the existing signature and sign this library now ?",
0621: "Libraries should be signed uniquely with your certificate",
0622: JOptionPane.YES_NO_OPTION);
0623: if (rep != JOptionPane.YES_OPTION)
0624: continue ll;
0625:
0626: deleteExistingSignatures = true;
0627:
0628: } else if (signs.isEmpty()) {
0629: rep = JOptionPane
0630: .showConfirmDialog(
0631: pmd,
0632: "The external library "
0633: + destLib.getName()
0634: + " is not signed."
0635: + "\nWebStart requires that this library is signed with the same key as the project jar file."
0636: + "\n\nShould tIDE sign this library now ?",
0637: "Libraries should be signed with your certificate",
0638: JOptionPane.YES_NO_OPTION);
0639: if (rep != JOptionPane.YES_OPTION)
0640: continue ll;
0641:
0642: deleteExistingSignatures = false;
0643: } else // there are 1 or more signatures, none has the same alias...
0644: {
0645: rep = JOptionPane
0646: .showConfirmDialog(
0647: pmd,
0648: "The external library "
0649: + destLib.getName()
0650: + "\nIs already signed with the alias(es) "
0651: + signs
0652: + "."
0653: + "\nThe file must be signed exclusively with your own key."
0654: + "\nOtherwise, WebStart will not be able to launch the application."
0655: + "\nShould tIDE remove the existing signatures and sign this library now ?",
0656: "Libraries should be signed uniquely with your certificate",
0657: JOptionPane.YES_NO_OPTION);
0658: if (rep != JOptionPane.YES_OPTION)
0659: continue ll;
0660:
0661: deleteExistingSignatures = true;
0662:
0663: }
0664:
0665: if (deleteExistingSignatures) {
0666: pmd
0667: .setCommentLabel("Removing existing signatures in "
0668: + destLib);
0669: JarSigner
0670: .removeAllExistingSignatures(destLib);
0671: }
0672:
0673: pmd.setCommentLabel("Signing " + destLib);
0674: Process proc = JarSigner.signJarFile_(destLib,
0675: jarSignerPanel.getKeyStorePath(),
0676: jarSignerPanel.getKeyStorePassword(),
0677: jarSignerPanel.getKeyStoreAlias());
0678: ProcessUtils.drainProcess(proc, true); // waits until completion !
0679: } catch (Exception e) {
0680: System.err
0681: .println("Problem occured during the export of "
0682: + destLib);
0683: e.printStackTrace();
0684: }
0685: }
0686:
0687: }
0688: }
0689: }
0690:
0691: /** All the steps are driven here, from collecting up to publishing
0692: * during collecting/creating of the main jar file, modality is set to true.
0693: * later steps are without modality.
0694: */
0695: private void createJarFile(final File dest) throws Exception {
0696: if (MainEditorFrame.instance == null)
0697: throw new Exception("The IDE is not started !");
0698:
0699: final ProjectSettings actualProject = MainEditorFrame.instance
0700: .getActualProject();
0701: MainEditorFrame.instance.outputPanels.selectToolsTab(true);
0702:
0703: // may be null
0704: final String mainClassName = actualProject
0705: .getMainClassJavaName();
0706:
0707: // look if class path items exists at destination
0708: if (advancedModeBT.isSelected()
0709: && (createIndex.isSelected() || this .createJNLP
0710: .isSelected())) {
0711: checkClassPathPresenceAtDestination(dest.getParentFile(),
0712: pmd);
0713: }
0714:
0715: pmd.setProgressSection("Collecting all classes");
0716: List<File> allClasses = new ArrayList<File>();
0717: FileUtils.getAllFilesRecurse(actualProject.getClasses_Home(),
0718: allClasses, false, true); // ignore dirs starting with "."
0719:
0720: int totCount = allClasses.size();
0721:
0722: if (this .includeSources.isSelected()) {
0723: pmd.setCommentLabel("Collecting all sources");
0724: List<SourceFile> allSources = MainEditorFrame.instance.sourcesTreePanel
0725: .getTreeModel().getAllSourceFiles(false); // without ignored
0726: totCount += allSources.size();
0727: }
0728:
0729: if (this .includeResources.isSelected()) {
0730: pmd.setCommentLabel("Collecting all resources");
0731: List<File> allResources = MainEditorFrame.instance.sourcesTreePanel
0732: .getTreeModel().getAllResourceFiles(null);
0733: totCount += allResources.size();
0734: }
0735:
0736: pmd.setProgressSection("Creating the archive");
0737:
0738: if (signArchive.isSelected()) {
0739: totCount *= 2;
0740: }
0741:
0742: pmd.setProgressBounds(totCount);
0743: pmd.setProgressSection("Adding the class files");
0744: pmd.resetStartTime();
0745:
0746: try {
0747: if (!dest.getParentFile().exists())
0748: dest.getParentFile().mkdirs();
0749:
0750: // the destination shoult NOT be in the classes or sources folder, otherwise we would produce an infinite
0751: // great archive (jar.exe does !)
0752: // => test
0753: if (dest.getAbsolutePath().startsWith(
0754: actualProject.getSources_Home().getAbsolutePath())) {
0755: throw new Exception(
0756: "The jar file cannot be located in the source path");
0757: }
0758:
0759: if (dest.getAbsolutePath().startsWith(
0760: actualProject.getClasses_Home().getAbsolutePath())) {
0761: throw new Exception(
0762: "The jar file cannot be located in the destination classes folder.");
0763: }
0764:
0765: Process proc = JarCreation
0766: .createClassesJarFile(
0767: actualProject.getJar_TOOL(),
0768: dest,
0769: actualProject.getClasses_Home(),
0770: mainClassName,
0771: actualProject.getClassPath(false, true), // aux Classpath
0772: (this .useCustomManifest.isSelected() ? this .customManifestPath
0773: .getPath()
0774: : null));
0775:
0776: MainEditorFrame.instance.outputPanels.processesManager
0777: .addProcess("Creating project jar file", proc, true);
0778: pmd.setProcessToOptionalKill(proc);
0779:
0780: // use the gobbler to create a progress (1 line per entry)
0781: StreamGobbler sg = new StreamGobbler(proc.getInputStream(),
0782: (MainEditorFrame.debug ? System.out : null), "Jar1");
0783: sg.start();
0784: sg.lineCounter.addCounterListener(new CounterListener() {
0785: public void counterUpdated(int c) {
0786: pmd.incrementProgress(1); // called for each line !
0787: }
0788: });
0789: // report the errors to the document
0790: StreamGobbler sg2 = new StreamGobbler(
0791: proc.getErrorStream(),
0792: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
0793: .createWriterForThisDocument(true), "Jar2");
0794: sg2.start();
0795:
0796: proc.waitFor();
0797: } catch (Exception e) {
0798: throw new Exception(
0799: "Cannot create project jar file\nError="
0800: + e.getMessage(), e);
0801: }
0802:
0803: pmd.setCommentLabel("");
0804:
0805: if (this .includeSources.isSelected()
0806: || this .includeResources.isSelected()) {
0807: if (pmd.getWasCancelled())
0808: throw new Exception("Cancelled");
0809:
0810: pmd.setProgressSection("Adding sources/resource files");
0811: try {
0812: final List<String> ignoredEndings = Arrays
0813: .asList(actualProject.getProperty(
0814: "resources_ign_ext", "").split("\\s")); // Without "*" !!
0815: System.out
0816: .println("Ignored endings: " + ignoredEndings);
0817: Process proc = JarCreation.addFilesFromSourceFolder(
0818: actualProject.getJar_TOOL(), dest,
0819: actualProject.getSources_Home(),
0820: this .includeSources.isSelected(),
0821: this .includeResources.isSelected(),
0822: ignoredEndings);
0823:
0824: if (proc == null) // null if no file to add...
0825: {
0826: //MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc.appendLine("No resources to add to the jar file.");
0827: } else {
0828:
0829: MainEditorFrame.instance.outputPanels.processesManager
0830: .addProcess(
0831: "Updating project jar file (add sources/resources)",
0832: proc, true);
0833: pmd.setProcessToOptionalKill(proc);
0834:
0835: StreamGobbler sg = new StreamGobbler(
0836: proc.getInputStream(),
0837: (MainEditorFrame.debug ? System.out : null),
0838: "Jar3");
0839: sg.start();
0840: sg.lineCounter
0841: .addCounterListener(new CounterListener() {
0842: public void counterUpdated(int c) {
0843: pmd.incrementProgress(1); // called for each line !
0844: }
0845: });
0846:
0847: StreamGobbler sg2 = new StreamGobbler(
0848: proc.getErrorStream(),
0849: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
0850: .createWriterForThisDocument(true),
0851: "Jar4");
0852: sg2.start();
0853:
0854: proc.waitFor(); // important !
0855: }
0856: } catch (Exception e) {
0857: throw new Exception(
0858: "Cannot add project sources/resources to jar file\nError="
0859: + e.getMessage(), e);
0860: }
0861: }
0862:
0863: if (advancedModeBT.isSelected()
0864: && createSourceArchiveCB.isSelected()) {
0865: pmd
0866: .setProgressSection("Creating a separate sources archive");
0867: List<SourceFile> allSources = MainEditorFrame.instance.sourcesTreePanel
0868: .getTreeModel().getAllSourceFiles(false); // without ignored
0869: List<File> allResources = null;
0870: if (this .includeResourcesInSrc.isSelected()) {
0871: allResources = MainEditorFrame.instance.sourcesTreePanel
0872: .getTreeModel().getAllResourceFiles(null); // *all*, even the ones not in the jar !
0873: }
0874: JarCreation.create_separate_SourcesZipFile(actualProject
0875: .getSources_Home(), allSources, allResources,
0876: srcDestination.getPath());
0877: }
0878:
0879: // no more essential, let the user make something else
0880: // yes...
0881: pmd.setModal(false);
0882: pmd.setVisible(false);
0883: pmd.setModal(false);
0884: //pmd.setTitle("Jar creation (second act)");
0885: pmd.setVisible(true);
0886:
0887: if (advancedModeBT.isSelected()
0888: && ProGuardSettingsDialog.isConfigured()
0889: && proguardCB.isSelected()) {
0890: pmd.setProgressSection("Obfuscation");
0891: if (mainClassName == null)
0892: throw new Exception(
0893: "Cannot obfuscate, a main class must be defined in the project.");
0894: ProGuardLauncher.obfuscate(dest, pmd, mainClassName);
0895: pmd.setCommentLabel("");
0896: }
0897:
0898: if (createIndex.isSelected() && advancedModeBT.isSelected()) {
0899: if (pmd.getWasCancelled())
0900: throw new Exception("Cancelled");
0901: pmd.setProgressSection("indexing jar files");
0902:
0903: try {
0904: Process proc2 = JarCreation.indexJarFile(actualProject
0905: .getJar_TOOL(), dest);
0906:
0907: MainEditorFrame.instance.outputPanels.processesManager
0908: .addProcess("Indexing project jar file", proc2,
0909: true);
0910: pmd.setProcessToOptionalKill(proc2);
0911:
0912: StreamGobbler sg3 = new StreamGobbler(proc2
0913: .getInputStream(),
0914: (MainEditorFrame.debug ? System.out : null),
0915: "JarIndexation");
0916: sg3.start();
0917: StreamGobbler sg4 = new StreamGobbler(
0918: proc2.getErrorStream(),
0919: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
0920: .createWriterForThisDocument(true),
0921: "JarIndexation");
0922: sg4.start();
0923:
0924: int rep = proc2.waitFor(); // important !
0925: if (rep != 0)
0926: throw new Exception(
0927: "Indexing failed, process terminated");
0928:
0929: if (sg4.lineCounter.getCount() > 0) {
0930: throw new Exception(
0931: "Indexing failed, error lines were produces");
0932: }
0933: } catch (Exception e) {
0934: //throw new Exception("Cannot index project jar file\nError="+e.getMessage(), e);
0935: // Not fatal: the index may have failed because libs not found...
0936: JOptionPane
0937: .showMessageDialog(
0938: this ,
0939: "Error: "
0940: + e.getMessage()
0941: + "\nHint: the target jar directory must contain all the libraries of the external classpath.",
0942: "jar indexing failed (not fatal)",
0943: JOptionPane.WARNING_MESSAGE);
0944: }
0945: }
0946:
0947: long origJarSize = dest.length();
0948: long packed200Size = -1;
0949:
0950: File pack200GzFile = new File(dest.getAbsolutePath()
0951: + ".pack.gz");
0952:
0953: if (createPack200.isSelected() && advancedModeBT.isSelected()
0954: && signArchive.isSelected()) {
0955: pmd
0956: .setProgressSection("Pack200 creation preparation (normalization)");
0957: // IMPORTANT: the not signed archive must be packed and nupacked (replacing original) and then signad and repacked
0958: // to conserve a valid signature after decompression, must be normalized !
0959: pmd.setProgressComment("creating a temp pack200");
0960: JarCreation.compress_pack200(dest, pack200GzFile);
0961:
0962: pmd.setProgressComment("decompacting the temp pack200");
0963: JarCreation.replaceJarWithPack200Decompressed(dest,
0964: pack200GzFile);
0965: pmd.setProgressComment("");
0966:
0967: if (pack200GzFile.exists())
0968: packed200Size = pack200GzFile.length();
0969:
0970: }
0971:
0972: if (signArchive.isSelected()) {
0973: if (pmd.getWasCancelled())
0974: throw new Exception("Cancelled");
0975: pmd.setProgressSection("Signing the jar file");
0976: Process proc = JarSigner.signJarFile_(dest, jarSignerPanel
0977: .getKeyStorePath(), jarSignerPanel
0978: .getKeyStorePassword(), jarSignerPanel
0979: .getKeyStoreAlias());
0980: pmd.setProcessToOptionalKill(proc);
0981: StreamGobbler sg3 = new StreamGobbler(
0982: proc.getInputStream(), (Writer) null, //(MainEditorFrame.debug ? System.out : null),
0983: "JarSigner_");
0984: sg3.start();
0985: sg3.lineCounter.addCounterListener(new CounterListener() {
0986: public void counterUpdated(int c) {
0987: pmd.incrementProgress(1); // called for each line !
0988: }
0989: });
0990: StreamGobbler sg4 = new StreamGobbler(
0991: proc.getErrorStream(),
0992: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
0993: .createWriterForThisDocument(true),
0994: "JarSigner#");
0995: sg4.start();
0996:
0997: proc.waitFor(); // important !
0998:
0999: origJarSize = dest.length();
1000: }
1001:
1002: // second run: create a pack200
1003: if (createPack200.isSelected() && advancedModeBT.isSelected()) {
1004: // IMPORTANT: the not signed archive must be packed and nupacked (replacing original) and then signad and repacked
1005: // to conserve a valid signature after decompression, must be normalized !
1006: pmd
1007: .setProgressSection("Creating the jar.pack200.gz archive");
1008: JarCreation.compress_pack200(dest, pack200GzFile);
1009: if (pack200GzFile.exists())
1010: packed200Size = pack200GzFile.length();
1011: }
1012:
1013: if (packed200Size > 1) {
1014: MainEditorFrame.instance.outputPanels.toolsOutputPanel.doc
1015: .appendLine("Pack200 archive is "
1016: + FileUtils.formatSize(packed200Size)
1017: + " ("
1018: + (int) (((origJarSize - packed200Size) * 100) / origJarSize)
1019: + "% shrink)");
1020: }
1021:
1022: if (this .createBatch.isSelected()
1023: && advancedModeBT.isSelected()) {
1024: pmd.setProgressSection("Generating the batch launcher");
1025: JarCreation.createBatchLauncher(actualProject
1026: .getJava_TOOL(), dest, actualProject
1027: .getRuntimeArgs(), actualProject.getAppArgs());
1028: }
1029:
1030: if (this .createJNLP.isSelected() && advancedModeBT.isSelected()) {
1031: pmd.setProgressSection("Generating the JNLP launcher");
1032: JarCreation.createJNLPLauncher(dest, actualProject
1033: .getRuntimeArgs(),
1034: actualProject.getAppArgs(),
1035: mainClassName,
1036: actualProject.getClassPath(false, true), // aux Classpath
1037: this .jNLPPanel.getCodeBase(), jNLPPanel.getTitle(),
1038: jNLPPanel.getVendor(), jNLPPanel.getHomePage(),
1039: jNLPPanel.getDescription(), jNLPPanel
1040: .getJavaVersion(), null, // directly taken from project jvm arguments jNLPPanel.getMaxMemoryUsage() );
1041: signArchive.isSelected());
1042: }
1043:
1044: // just as info
1045: if (actualProject.getProperty("JNLP_codebase", null) != null) {
1046: MainEditorFrame.instance.outputPanels.tideOutputPanel.doc
1047: .appendLine("Project homepage: "
1048: + actualProject.getProperty(
1049: "JNLP_codebase", "??")
1050: + actualProject.getProperty(
1051: "JNLP_homepage", "index.html"));
1052: }
1053:
1054: if (advancedModeBT.isSelected()) {
1055: if (publish.isSelected()) {
1056: pmd.setVisible(false);
1057: pmd.setProgressSection("Publishing");
1058:
1059: EventQueue.invokeLater(new Runnable() {
1060: public void run() {
1061: ExportDialog ed = new ExportDialog(
1062: MainEditorFrame.instance,
1063: actualProject, false);
1064: }
1065: });
1066: }
1067: }
1068: }
1069:
1070: /*test
1071: public static void main(String[] aa)
1072: {
1073: List<String> ignoredEndings = Arrays.asList(".tmp ,txt".split("\\s") );
1074: System.out.println("Ignored endings: "+ignoredEndings);
1075:
1076: try
1077: {
1078: // buggy...
1079: File f1 = new File("c:\\temp/");
1080: File f2 = new File(new File("c:/temp/../temp").toURI());
1081: System.out.println(""+f1.toURI());
1082: System.out.println(""+f2.toURI());
1083: System.out.println(""+f1.toURI().equals(f2.toURI()));
1084: }
1085: catch(Exception e)
1086: {
1087: e.printStackTrace();
1088: }
1089:
1090: Locale.setDefault(Locale.ENGLISH);
1091: JFrame f = new JFrame("Test");
1092: f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
1093: ProjectSettings props = new ProjectSettings();
1094: new JarCreationDialog(f, props);
1095: System.exit(0);
1096: }*/
1097:
1098: }
|