001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU General
007: * Public License Version 2 only ("GPL") or the Common Development and Distribution
008: * License("CDDL") (collectively, the "License"). You may not use this file except in
009: * compliance with the License. You can obtain a copy of the License at
010: * http://www.netbeans.org/cddl-gplv2.html or nbbuild/licenses/CDDL-GPL-2-CP. See the
011: * License for the specific language governing permissions and limitations under the
012: * License. When distributing the software, include this License Header Notice in
013: * each file and include the License file at nbbuild/licenses/CDDL-GPL-2-CP. Sun
014: * designates this particular file as subject to the "Classpath" exception as
015: * provided by Sun in the GPL Version 2 section of the License file that
016: * accompanied this code. If applicable, add the following below the License Header,
017: * with the fields enclosed by brackets [] replaced by your own identifying
018: * information: "Portions Copyrighted [year] [name of copyright owner]"
019: *
020: * Contributor(s):
021: *
022: * The Original Software is NetBeans. The Initial Developer of the Original Software
023: * is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun Microsystems, Inc. All
024: * Rights Reserved.
025: *
026: * If you wish your version of this file to be governed by only the CDDL or only the
027: * GPL Version 2, indicate your decision by adding "[Contributor] elects to include
028: * this software in this distribution under the [CDDL or GPL Version 2] license." If
029: * you do not indicate a single choice of license, a recipient has the option to
030: * distribute your version of this file under either the CDDL, the GPL Version 2 or
031: * to extend the choice of license to its licensees as provided above. However, if
032: * you add GPL Version 2 code and therefore, elected the GPL Version 2 license, then
033: * the option applies only if the new code is made subject to such option by the
034: * copyright holder.
035: */
036:
037: package org.netbeans.installer.wizard.components.actions;
038:
039: import java.io.File;
040: import java.io.FileInputStream;
041: import java.io.FileOutputStream;
042: import java.io.IOException;
043: import java.io.InputStream;
044: import java.net.URI;
045: import java.net.URISyntaxException;
046: import java.util.Enumeration;
047: import java.util.List;
048: import java.util.Properties;
049: import java.util.jar.JarEntry;
050: import java.util.jar.JarFile;
051: import java.util.jar.JarOutputStream;
052: import org.netbeans.installer.Installer;
053: import org.netbeans.installer.product.components.Group;
054: import org.netbeans.installer.product.components.Product;
055: import org.netbeans.installer.product.Registry;
056: import org.netbeans.installer.product.filters.RegistryFilter;
057: import org.netbeans.installer.product.filters.SubTreeFilter;
058: import org.netbeans.installer.utils.FileUtils;
059: import org.netbeans.installer.utils.LogManager;
060: import org.netbeans.installer.utils.helper.Status;
061: import org.netbeans.installer.utils.StringUtils;
062: import org.netbeans.installer.utils.exceptions.FinalizationException;
063: import org.netbeans.installer.utils.ErrorManager;
064: import org.netbeans.installer.utils.FileProxy;
065: import org.netbeans.installer.utils.ResourceUtils;
066: import org.netbeans.installer.utils.StreamUtils;
067: import org.netbeans.installer.utils.XMLUtils;
068: import org.netbeans.installer.utils.exceptions.XMLException;
069: import org.netbeans.installer.utils.helper.EngineResources;
070: import org.netbeans.installer.utils.helper.ExtendedUri;
071: import org.netbeans.installer.utils.helper.Platform;
072: import org.netbeans.installer.utils.progress.Progress;
073: import org.netbeans.installer.wizard.components.WizardAction;
074:
075: /**
076: *
077: * @author Kirill Sorokin
078: */
079: public class CreateBundleAction extends WizardAction {
080: /////////////////////////////////////////////////////////////////////////////////
081: // Constants
082: public static final String DEFAULT_TITLE = ResourceUtils.getString(
083: CreateBundleAction.class, "CBA.title"); // NOI18N
084: public static final String DEFAULT_DESCRIPTION = ResourceUtils
085: .getString(CreateBundleAction.class, "CBA.description"); // NOI18N
086: public static final String DEFAULT_PROGRESS_CREATE_BUNDLE_TITLE = ResourceUtils
087: .getString(CreateBundleAction.class,
088: "CBA.progress.create.bundle.title");//NOI18N
089: public static final String DEFAULT_PROGRESS_ADD_ENGINE_DETAIL = ResourceUtils
090: .getString(CreateBundleAction.class,
091: "CBA.progress.add.engine.detail");//NOI18N
092: public static final String DEFAULT_PROGRESS_ADD_PRODUCT_DETAIL = ResourceUtils
093: .getString(CreateBundleAction.class,
094: "CBA.progress.add.product.detail");//NOI18N
095: public static final String DEFAULT_PROGRESS_ADD_GROUP_DETAIL = ResourceUtils
096: .getString(CreateBundleAction.class,
097: "CBA.progress.add.group.detail");//NOI18N
098: public static final String DEFAULT_ERROR_FAILED_CREATE_BUNDLE = ResourceUtils
099: .getString(CreateBundleAction.class,
100: "CBA.error.failed.create.bundle");//NOI18N
101:
102: public static final String PROGRESS_CREATE_BUNDLE_TITLE_PROPERTY = "progress.create.bundle.title";//NOI18N
103: public static final String PROGRESS_ADD_ENGINE_DETAIL_PROPERTY = "progress.add.engine.detail";//NOI18N
104: public static final String PROGRESS_ADD_PRODUCT_DETAIL_PROPERTY = "progress.add.product.detail";//NOI18N
105: public static final String PROGRESS_ADD_GROUP_DETAIL_PROPERTY = "progress.add.group.detail";//NOI18N
106: public static final String ERROR_FAILED_CREATE_BUNDLE_PROPERTY = "error.failed.create.bundle";//NOI18N
107:
108: /////////////////////////////////////////////////////////////////////////////////
109: // Instance
110: private Progress progress;
111:
112: public CreateBundleAction() {
113: setProperty(TITLE_PROPERTY, DEFAULT_TITLE);
114: setProperty(DESCRIPTION_PROPERTY, DEFAULT_DESCRIPTION);
115: setProperty(PROGRESS_CREATE_BUNDLE_TITLE_PROPERTY,
116: DEFAULT_PROGRESS_CREATE_BUNDLE_TITLE);
117: setProperty(PROGRESS_ADD_ENGINE_DETAIL_PROPERTY,
118: DEFAULT_PROGRESS_ADD_ENGINE_DETAIL);
119: setProperty(PROGRESS_ADD_PRODUCT_DETAIL_PROPERTY,
120: DEFAULT_PROGRESS_ADD_PRODUCT_DETAIL);
121: setProperty(PROGRESS_ADD_GROUP_DETAIL_PROPERTY,
122: DEFAULT_PROGRESS_ADD_GROUP_DETAIL);
123: }
124:
125: public void execute() {
126: long started = System.currentTimeMillis();
127:
128: LogManager.log("creating bundle");
129: LogManager
130: .log(" initializing registry and required products");
131: final Registry registry = Registry.getInstance();
132: final RegistryFilter filter = new SubTreeFilter(registry
133: .getProductsToInstall());
134: final List<Product> products = registry.queryProducts(filter);
135: final List<Group> groups = registry.queryGroups(filter);
136:
137: LogManager
138: .log(" products to install: "
139: + StringUtils.asString(registry
140: .getProductsToInstall()));
141: LogManager.log(" selected products: "
142: + StringUtils.asString(products));
143: LogManager.log(" selected groups: "
144: + StringUtils.asString(groups));
145:
146: int percentageChunk = Progress.START;
147: int percentageLeak = Progress.COMPLETE;
148:
149: if (products.size() + groups.size() > 0) {
150: percentageChunk = Progress.COMPLETE
151: / (products.size() + groups.size());
152: percentageLeak = Progress.COMPLETE
153: % (products.size() + groups.size());
154: }
155:
156: final String targetPath = System
157: .getProperty(Registry.CREATE_BUNDLE_PATH_PROPERTY);
158: final File targetFile = new File(targetPath);
159:
160: progress = new Progress();
161:
162: getWizardUi().setProgress(progress);
163:
164: JarFile engine = null;
165: JarOutputStream output = null;
166:
167: try {
168: LogManager.indent();
169: LogManager.log("... creating bundle file at " + targetFile);
170: progress.setTitle(StringUtils.format(
171: getProperty(PROGRESS_CREATE_BUNDLE_TITLE_PROPERTY),
172: targetFile));
173: progress
174: .setDetail(StringUtils
175: .format(getProperty(PROGRESS_ADD_ENGINE_DETAIL_PROPERTY)));
176:
177: engine = new JarFile(Installer
178: .cacheInstallerEngine(new Progress()));
179: output = new JarOutputStream(new FileOutputStream(
180: targetFile));
181:
182: // transfer the engine, skipping existing bundled components
183: final Enumeration entries = engine.entries();
184: LogManager
185: .log("... adding entries from the engine. Total : "
186: + engine.size());
187: while (entries.hasMoreElements()) {
188: final JarEntry entry = (JarEntry) entries.nextElement();
189:
190: // check for cancel status
191: if (isCanceled())
192: return;
193:
194: final String name = entry.getName();
195:
196: // skip the (possibly) already cached data
197: if (name.startsWith(EngineResources.DATA_DIRECTORY)) {
198: continue;
199: }
200:
201: // skip the signing information if it exists
202: if (name.startsWith("META-INF/")
203: && !name.equals("META-INF/")
204: && !name.equals("META-INF/MANIFEST.MF")) {
205: continue;
206: }
207:
208: output.putNextEntry(entry);
209: StreamUtils.transferData(engine.getInputStream(entry),
210: output);
211: }
212:
213: output.putNextEntry(new JarEntry(
214: EngineResources.DATA_DIRECTORY + "/"));
215:
216: // transfer the engine files list
217: output.putNextEntry(new JarEntry(
218: EngineResources.ENGINE_CONTENTS_LIST));
219: StreamUtils.transferData(ResourceUtils
220: .getResource(EngineResources.ENGINE_CONTENTS_LIST),
221: output);
222:
223: // load default engine properties and set all components to be installed by default
224: String[] contents = StringUtils
225: .splitByLines(StreamUtils
226: .readStream(ResourceUtils
227: .getResource(EngineResources.ENGINE_CONTENTS_LIST)));
228: for (String entry : contents) {
229: if (entry
230: .matches(EngineResources.ENGINE_PROPERTIES_PATTERN)) {
231: Properties engineProps = new Properties();
232: engineProps.load(ResourceUtils.getResource(entry));
233:
234: if (System
235: .getProperty(Installer.BUNDLE_PROPERTIES_FILE_PROPERTY) != null) {
236: Properties pr = new Properties();
237: InputStream is = new FileInputStream(
238: new File(
239: System
240: .getProperty(Installer.BUNDLE_PROPERTIES_FILE_PROPERTY)));
241: pr.load(is);
242: is.close();
243: engineProps.putAll(pr);
244: }
245:
246: engineProps.setProperty(
247: Registry.SUGGEST_INSTALL_PROPERTY,
248: StringUtils.EMPTY_STRING + true);
249: output.putNextEntry(new JarEntry(entry));
250: engineProps.store(output, null);
251: }
252: }
253:
254: progress.addPercentage(percentageLeak);
255: LogManager.log("... adding " + products.size()
256: + " products");
257: for (Product product : products) {
258: // check for cancel status
259: if (isCanceled())
260: return;
261:
262: progress
263: .setDetail(StringUtils
264: .format(
265: getProperty(PROGRESS_ADD_PRODUCT_DETAIL_PROPERTY),
266: product.getDisplayName()));
267:
268: LogManager.log("... adding product : "
269: + product.getDisplayName());
270:
271: final List<Platform> platforms = product.getPlatforms();
272: final String entryPrefix = EngineResources.DATA_DIRECTORY
273: + "/"
274: + product.getUid()
275: + "/"
276: + product.getVersion()
277: + "/"
278: + StringUtils.asString(product.getPlatforms(),
279: " ");
280: final String uriPrefix = FileProxy.RESOURCE_SCHEME_PREFIX
281: + EngineResources.DATA_DIRECTORY
282: + "/"
283: + product.getUid()
284: + "/"
285: + product.getVersion()
286: + "/" + StringUtils.asString(platforms, "%20");
287:
288: // create the required directories structure
289: output.putNextEntry(new JarEntry(
290: EngineResources.DATA_DIRECTORY + "/"
291: + product.getUid() + "/"));
292: output.putNextEntry(new JarEntry(
293: EngineResources.DATA_DIRECTORY
294: + "/"
295: + product.getUid()
296: + "/"
297: + product.getVersion()
298: + "/"
299: + StringUtils.asString(product
300: .getPlatforms(), " ") + "/"));
301: output.putNextEntry(new JarEntry(
302: EngineResources.DATA_DIRECTORY
303: + "/"
304: + product.getUid()
305: + "/"
306: + product.getVersion()
307: + "/"
308: + StringUtils.asString(product
309: .getPlatforms(), " ") + "/"
310: + "logic" + "/"));
311: output.putNextEntry(new JarEntry(
312: EngineResources.DATA_DIRECTORY
313: + "/"
314: + product.getUid()
315: + "/"
316: + product.getVersion()
317: + "/"
318: + StringUtils.asString(product
319: .getPlatforms(), " ") + "/"
320: + "data" + "/"));
321:
322: // transfer the icon
323: output.putNextEntry(new JarEntry(entryPrefix
324: + "/icon.png"));
325: StreamUtils.transferFile(new File(product.getIconUri()
326: .getLocal()), output);
327:
328: // correct the local uri for the icon, so it gets saved correctly in
329: // the registry file
330: product.getIconUri().setLocal(
331: new URI(uriPrefix + "/icon.png"));
332:
333: // transfer the configuration logic files
334: final List<ExtendedUri> logicUris = product
335: .getLogicUris();
336: for (int i = 0; i < logicUris.size(); i++) {
337: final ExtendedUri logicUri = logicUris.get(i);
338:
339: // check for cancel status
340: if (isCanceled())
341: return;
342:
343: // transfer the file
344: output.putNextEntry(new JarEntry(entryPrefix
345: + "/logic/logic," + (i + 1) + ".jar"));
346: StreamUtils.transferFile(new File(logicUri
347: .getLocal()), output);
348:
349: // delete the downloaded file -- we need to delete it only in
350: // case it has been really downloaded, i.e. the local URI is
351: // different from the main remote one and the alternates
352: if (!logicUri.getLocal().equals(
353: logicUri.getRemote())
354: && !logicUri.getAlternates().contains(
355: logicUri.getLocal())) {
356: FileUtils.deleteFile(new File(logicUri
357: .getLocal()));
358: }
359:
360: // correct the local uri, so it gets saved correctly
361: logicUris.get(i).setLocal(
362: new URI(uriPrefix + "/logic/logic,"
363: + (i + 1) + ".jar"));
364: }
365:
366: // transfer the installation data files
367: final List<ExtendedUri> dataUris = product
368: .getDataUris();
369: for (int i = 0; i < dataUris.size(); i++) {
370: final ExtendedUri dataUri = dataUris.get(i);
371:
372: // check for cancel status
373: if (isCanceled())
374: return;
375:
376: // transfer the file
377: output.putNextEntry(new JarEntry(entryPrefix
378: + "/data/data," + (i + 1) + ".jar"));
379: StreamUtils.transferFile(new File(dataUris.get(i)
380: .getLocal()), output);
381:
382: // delete the downloaded file -- we need to delete it only in
383: // case it has been really downloaded, i.e. the local URI is
384: // different from the main remote one and the alternates
385: if (!dataUri.getLocal().equals(dataUri.getRemote())
386: && !dataUri.getAlternates().contains(
387: dataUri.getLocal())) {
388: FileUtils.deleteFile(new File(dataUri
389: .getLocal()));
390: }
391:
392: // correct the local uri, so it gets saved correctly
393: dataUris.get(i).setLocal(
394: new URI(uriPrefix + "/data/data," + (i + 1)
395: + ".jar"));
396: }
397:
398: // correct the product's status, so it gets saved correctly in the
399: // registry file
400: product.setStatus(Status.NOT_INSTALLED);
401:
402: // increment the progress percentage
403: progress.addPercentage(percentageChunk);
404: }
405:
406: LogManager.log("... adding " + groups.size() + " groups");
407: for (Group group : groups) {
408: // check for cancel status
409: if (isCanceled())
410: return;
411:
412: // we should skip the registry root, as it is a somewhat artificial
413: // node and does not have any meaning
414: if (group.equals(registry.getRegistryRoot())) {
415: continue;
416: }
417:
418: progress
419: .setDetail(StringUtils
420: .format(
421: getProperty(PROGRESS_ADD_GROUP_DETAIL_PROPERTY),
422: group.getDisplayName()));
423: LogManager.log("... adding group : "
424: + group.getDisplayName());
425: final String entryPrefix = EngineResources.DATA_DIRECTORY
426: + "/" + group.getUid();
427: final String uriPrefix = FileProxy.RESOURCE_SCHEME_PREFIX
428: + EngineResources.DATA_DIRECTORY
429: + "/"
430: + group.getUid();
431:
432: // create the required directories structure
433: output.putNextEntry(new JarEntry(
434: EngineResources.DATA_DIRECTORY + "/"
435: + group.getUid() + "/"));
436:
437: // transfer the icon
438: output.putNextEntry(new JarEntry(entryPrefix
439: + "/icon.png"));
440: StreamUtils.transferFile(new File(group.getIconUri()
441: .getLocal()), output);
442:
443: // correct the local uri for the icon, so it gets saved correctly in
444: // the registry file
445: group.getIconUri().setLocal(
446: new URI(uriPrefix + "/icon.png"));
447:
448: // increment the progress percentage
449: progress.addPercentage(percentageChunk);
450: }
451:
452: // check for cancel status
453: if (isCanceled())
454: return;
455:
456: // serialize the registry: get the document and save it to the jar file
457: output.putNextEntry(new JarEntry(
458: EngineResources.DATA_DIRECTORY + "/registry.xml"));
459: XMLUtils.saveXMLDocument(registry.getRegistryDocument(
460: filter, false, true, true), output);
461:
462: // finally perform some minor cleanup to avoid errors later in the main
463: // registry finalization - we set the local uri to be null, to avoid
464: // cleanup attempts (they would fail, as the local uris now look like
465: // resource:<...>
466: for (Product product : products) {
467: for (ExtendedUri uri : product.getLogicUris()) {
468: uri.setLocal(null);
469: }
470: for (ExtendedUri uri : product.getDataUris()) {
471: uri.setLocal(null);
472: }
473: }
474:
475: // set the products' status back to installed so that they are
476: // correctly mentioned in the state file
477: for (Product product : products) {
478: product.setStatus(Status.INSTALLED);
479: }
480: } catch (IOException e) {
481: ErrorManager
482: .notifyError(
483: getProperty(ERROR_FAILED_CREATE_BUNDLE_PROPERTY),
484: e);
485: } catch (XMLException e) {
486: ErrorManager
487: .notifyError(
488: getProperty(ERROR_FAILED_CREATE_BUNDLE_PROPERTY),
489: e);
490: } catch (FinalizationException e) {
491: ErrorManager
492: .notifyError(
493: getProperty(ERROR_FAILED_CREATE_BUNDLE_PROPERTY),
494: e);
495: } catch (URISyntaxException e) {
496: ErrorManager
497: .notifyError(
498: getProperty(ERROR_FAILED_CREATE_BUNDLE_PROPERTY),
499: e);
500: } finally {
501: if (engine != null) {
502: try {
503: engine.close();
504: } catch (IOException e) {
505: ErrorManager.notifyDebug(
506: "Failed to close the stream", e);
507: }
508: }
509: if (output != null) {
510: try {
511: output.close();
512: } catch (IOException e) {
513: ErrorManager.notifyDebug(
514: "Failed to close the stream", e);
515: }
516: }
517: long seconds = System.currentTimeMillis() - started;
518: LogManager.log("... generating bundle finished");
519: LogManager.log("[bundle] Time : " + (seconds / 1000) + "."
520: + (seconds % 1000) + " seconds");
521: LogManager.unindent();
522: }
523: }
524:
525: @Override
526: public void cancel() {
527: super .cancel();
528:
529: if (progress != null) {
530: progress.setCanceled(true);
531: }
532: }
533: }
|