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.utils.system.launchers.impl;
038:
039: import java.io.File;
040: import java.io.FileOutputStream;
041: import java.io.IOException;
042: import java.io.InputStream;
043: import java.util.ArrayList;
044: import java.util.Enumeration;
045: import java.util.LinkedList;
046: import java.util.List;
047: import java.util.MissingResourceException;
048: import java.util.PropertyResourceBundle;
049: import org.netbeans.installer.utils.FileUtils;
050: import org.netbeans.installer.utils.LogManager;
051: import org.netbeans.installer.utils.ResourceUtils;
052: import org.netbeans.installer.utils.StringUtils;
053: import org.netbeans.installer.utils.helper.JavaCompatibleProperties;
054: import org.netbeans.installer.utils.system.launchers.LauncherProperties;
055: import org.netbeans.installer.utils.system.launchers.LauncherResource;
056: import org.netbeans.installer.utils.progress.Progress;
057: import org.netbeans.installer.utils.system.NativeUtils;
058:
059: /**
060: *
061: * @author Dmitry Lipin
062: */
063: public class ExeLauncher extends CommonLauncher {
064: private static final String EXE_EXT = ".exe"; //NOI18N
065: private static final int EXE_STUB_FILL_SIZE = 420000;
066: private static final long MAXDWORD = 4294967296L; // actually it is MAXDWORD + 1
067:
068: public static final String DEFAULT_WINDOWS_RESOURCE_SUFFIX = NativeUtils.NATIVE_LAUNCHER_RESOURCE_SUFFIX
069: + "windows/"; //NOI18N
070: public static final String I18N = "i18n"; //NOI18N
071: public static final String EXE_LAUNCHER_STUB_NAME = "nlw.exe"; //NOI18N
072:
073: public static final String EXE_LAUNCHER_STUB = DEFAULT_WINDOWS_RESOURCE_SUFFIX
074: + EXE_LAUNCHER_STUB_NAME;
075: public static final String DEFAULT_WINDOWS_RESOURCE_I18N = DEFAULT_WINDOWS_RESOURCE_SUFFIX
076: + I18N;
077: /**
078: * See <code>ShLauncher#MIN_JAVA_VERSION_UNIX</code> for details.
079: */
080: public static final String MIN_JAVA_VERSION_WINDOWS = "1.5.0_03";
081: public static final String MIN_JAVA_VERSION_WINDOWS_VISTA = "1.5.0_11";
082: public static final String OSNAME_WINDOWS_XP = "XP";
083: public static final String OSNAME_WINDOWS_VISTA = "Vista";
084: public static final String OSNAME_WINDOWS_2K = "2000";
085: public static final String OSNAME_WINDOWS_2K3 = "2003";
086:
087: public ExeLauncher(LauncherProperties props) {
088: super (props);
089: }
090:
091: public void initialize() throws IOException {
092: LogManager.log("Checking EXE launcher parameters..."); //NOI18N
093: checkAllParameters();
094: }
095:
096: public File create(Progress progress) throws IOException {
097:
098: FileOutputStream fos = null;
099: try {
100: progress.setPercentage(Progress.START);
101: fos = new FileOutputStream(outputFile, false);
102:
103: long bundledSize = getBundledFilesSize();
104: long total = bundledSize;
105: if (stubFile != null) {
106: total += FileUtils.getSize(stubFile);
107: }
108:
109: addExeInitialStub(fos, progress, total);
110:
111: LogManager.log("Adding i18n..."); //NOI18N
112: addI18NStrings(fos);
113:
114: // jvm args
115: addData(fos, jvmArguments, true);
116: LogManager.log("JVM Arguments: " + //NOI18N
117: ((jvmArguments != null) ? StringUtils.asString(
118: jvmArguments, " ")
119: : StringUtils.EMPTY_STRING));
120:
121: // app args
122: addData(fos, appArguments, true);
123: LogManager.log("App Arguments: " + //NOI18N
124: ((appArguments != null) ? StringUtils.asString(
125: appArguments, " ")
126: : StringUtils.EMPTY_STRING));
127:
128: addData(fos, mainClass, true);
129: LogManager.log("Main Class : " + //NOI18N
130: mainClass);
131:
132: addData(fos, testJVMClass, true);
133: LogManager.log("TestJVM Class : " + //NOI18N
134: testJVMClass);
135:
136: // add java compatibility properties number
137: addNumber(fos, new Long("" + compatibleJava.size())
138: .longValue());
139: addJavaCompatibleProperties(fos);
140:
141: //add overall bundled number and size
142: addNumber(fos, getBundledFilesNumber());
143: addNumber(fos, bundledSize, true);
144:
145: //add testJVM section
146: addFileSection(fos, testJVMFile, progress, total);
147:
148: //java locations that the launcher should see at first
149: LogManager
150: .log("Adding JVM external locations and bundled files"); //NOI18N
151: addData(fos, jvms, progress, total);
152:
153: // number of bundled and external jars
154: LogManager.log("Adding bundled and external jars"); //NOI18N
155: addData(fos, jars, progress, total);
156:
157: // a number of other resources
158: LogManager.log("Adding other resources"); //NOI18N
159: addData(fos, otherResources, progress, total);
160:
161: } catch (IOException ex) {
162: LogManager.log(ex);
163: try {
164: if (fos != null) {
165: fos.close();
166: }
167: } catch (IOException e) {
168: LogManager.log(e);
169: }
170:
171: try {
172: FileUtils.deleteFile(outputFile);
173: } catch (IOException e) {
174: LogManager.log(e);
175: }
176: fos = null;
177: } finally {
178: if (fos != null) {
179: try {
180: fos.close();
181: } catch (IOException ex) {
182: LogManager.log(ex.toString());
183: throw ex;
184: }
185: }
186: progress.setPercentage(Progress.COMPLETE);
187: }
188:
189: return outputFile;
190: }
191:
192: public String[] getExecutionCommand() {
193: return new String[] { outputFile.getAbsolutePath() };
194: }
195:
196: public List<JavaCompatibleProperties> getDefaultCompatibleJava() {
197: List<JavaCompatibleProperties> list = new ArrayList<JavaCompatibleProperties>();
198: list.add(new JavaCompatibleProperties(
199: MIN_JAVA_VERSION_WINDOWS_VISTA, null, null,
200: OSNAME_WINDOWS_VISTA, null));
201: list.add(new JavaCompatibleProperties(MIN_JAVA_VERSION_WINDOWS,
202: null, null, OSNAME_WINDOWS_XP, null));
203: list.add(new JavaCompatibleProperties(MIN_JAVA_VERSION_WINDOWS,
204: null, null, OSNAME_WINDOWS_2K, null));
205: list.add(new JavaCompatibleProperties(MIN_JAVA_VERSION_WINDOWS,
206: null, null, OSNAME_WINDOWS_2K3, null));
207: return list;
208: }
209:
210: private String changeJavaPropertyCounter(final String string) {
211: String str = string;
212: if (str != null) {
213: int counter = 0;
214: while (str.indexOf(getJavaCounter(counter)) != -1) {
215: str = str.replace(getJavaCounter(counter++), "%s");
216: }
217: }
218: return str;
219: }
220:
221: private void addExeInitialStub(FileOutputStream fos,
222: Progress progress, long total) throws IOException {
223: long stubSize;
224: if (stubFile != null) {
225: checkParameter("stub file", stubFile); //NOI18N
226: stubSize = addData(fos, stubFile, progress, total);
227: } else {
228: stubSize = addData(fos, ResourceUtils
229: .getResource(EXE_LAUNCHER_STUB), progress, 0);
230: }
231: long length = EXE_STUB_FILL_SIZE - stubSize;
232: for (long i = 0; i < length; i++) {
233: addData(fos); // fill with some chars
234: }
235: }
236:
237: private void addI18NStrings(FileOutputStream fos)
238: throws IOException {
239: addNumber(fos, i18nMap.size()); // number of locales
240:
241: PropertyResourceBundle defaultBundle = i18nMap.get("");
242: //properties names
243: List<String> props = new LinkedList<String>();
244: Enumeration<String> en = defaultBundle.getKeys();
245: long numberOfProperties = 0;
246: while (en.hasMoreElements()) {
247: en.nextElement();
248: numberOfProperties++;
249: }
250: addNumber(fos, numberOfProperties); // number of properties
251:
252: String propertyName;
253: en = defaultBundle.getKeys();
254: while (en.hasMoreElements()) {
255: propertyName = en.nextElement();
256: props.add(propertyName);
257: addData(fos, propertyName, false); // save property name as ascii
258: }
259:
260: addData(fos, defaultBundle, null, StringUtils.EMPTY_STRING,
261: props);
262: i18nMap.remove(StringUtils.EMPTY_STRING);
263: Object[] locales = i18nMap.keySet().toArray();
264:
265: for (int i = 0; i < locales.length; i++) {
266: addData(fos, i18nMap.get(locales[i]), defaultBundle,
267: (String) locales[i], props);
268: }
269: }
270:
271: private void addJavaCompatibleProperties(FileOutputStream fos)
272: throws IOException {
273: LogManager.log("Total compatible java properties : "
274: + compatibleJava.size()); //NOI18N
275: LogManager.indent();
276: for (int i = 0; i < compatibleJava.size(); i++) {
277: // min and max jvm version
278: JavaCompatibleProperties prop = compatibleJava.get(i);
279: LogManager.log("... adding compatible jvm [" + i + "] : "
280: + prop.toString()); //NOI18N
281:
282: addData(fos, prop.getMinVersion(), false);
283: addData(fos, prop.getMaxVersion(), false);
284: addData(fos, prop.getVendor(), false);
285: addData(fos, prop.getOsName(), false);
286: addData(fos, prop.getOsArch(), false);
287: }
288: LogManager.unindent();
289: }
290:
291: private void addNumber(FileOutputStream fos, long number)
292: throws IOException {
293: fos.write(Long.toString(number).getBytes());
294: fos.write(0);
295: }
296:
297: private void addNumber(FileOutputStream fos, long number,
298: boolean separateBits) throws IOException {
299: if (separateBits) {
300: addNumber(fos, number % MAXDWORD);
301: addNumber(fos, (number - (number % MAXDWORD)) / MAXDWORD);
302: } else {
303: addNumber(fos, number);
304: }
305: }
306:
307: private void addData(FileOutputStream fos, boolean isTrue)
308: throws IOException {
309: addNumber(fos, isTrue ? 1L : 0L);
310: }
311:
312: private void addData(FileOutputStream fos,
313: List<LauncherResource> list, Progress progress, long total)
314: throws IOException {
315: addNumber(fos, list.size());
316: //add every entry section
317: LogManager.log("... overall number of files : " + //NOI18N
318: list.size());//NOI18N
319: for (LauncherResource file : list) {
320: LogManager.log(" adding file " + //NOI18N
321: file.getPath());//NOI18N
322: addFileSection(fos, file, progress, total);
323: }
324: }
325:
326: private void addData(FileOutputStream fos, List<String> strings,
327: boolean isUnicode) throws IOException {
328: addData(fos, strings.toArray(new String[0]), isUnicode);
329: }
330:
331: private void addData(FileOutputStream fos, String[] strings,
332: boolean isUnicode) throws IOException {
333:
334: if (strings != null) {
335: addNumber(fos, new Integer(strings.length).longValue()); // number of array elements
336: for (String s : strings) {
337: addData(fos, s, isUnicode);
338: }
339: } else {
340: addNumber(fos, 0L); // no elements
341: }
342:
343: }
344:
345: private void addFileSection(FileOutputStream fos,
346: LauncherResource file, Progress progress, long total)
347: throws IOException {
348: addNumber(fos, file.getPathType().toLong());
349: String path;
350: if (file.isBasedOnResource()) {
351: path = file.getPathType().getPathString(
352: ResourceUtils.getResourceFileName(file.getPath()));
353: } else {
354: path = file.getAbsolutePath();
355: }
356: addData(fos, path, true);
357:
358: if (file.isBundled()) {
359: addNumber(fos, file.getSize(), true);
360: InputStream is = null;
361: try {
362: is = file.getInputStream();
363: addNumber(fos, FileUtils.getCrc32(is), false);
364: is.close();
365: is = file.getInputStream();
366: addData(fos, is, progress, total);
367: is.close();
368: is = null;
369: } finally {
370: if (is != null) {
371: try {
372: is.close();
373: } catch (IOException e) {
374: LogManager.log(e);
375: }
376: }
377: }
378: }
379: }
380:
381: private void addData(FileOutputStream fos,
382: PropertyResourceBundle bundle,
383: PropertyResourceBundle backupBundle, String localeName,
384: List<String> propertiesNames) throws IOException {
385: String propertyName;
386: String localizedString;
387: addData(fos, localeName, true);
388: Enumeration<String> en = bundle.getKeys();
389: for (int i = 0; i < propertiesNames.size(); i++) {
390: String str = null;
391: try {
392: str = bundle.getString(propertiesNames.get(i));
393: } catch (MissingResourceException e) {
394: if (backupBundle != null) {
395: str = backupBundle
396: .getString(propertiesNames.get(i));
397: }
398: }
399: str = changeJavaPropertyCounter(str);
400: addData(fos, str, true); // localized string as UNICODE
401:
402: }
403: }
404:
405: private void addData(FileOutputStream fos, String str,
406: boolean isUnicode) throws IOException {
407: if (str != null) {
408: addString(fos, str, isUnicode);
409: }
410: if (isUnicode) {
411: fos.write(0);
412: }
413: fos.write(0);
414: }
415:
416: public String getExtension() {
417: return EXE_EXT;
418: }
419:
420: protected String getI18NResourcePrefix() {
421: return DEFAULT_WINDOWS_RESOURCE_SUFFIX;
422: }
423: }
|