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.FileInputStream;
041: import java.io.FileOutputStream;
042: import java.io.InputStream;
043: import java.io.IOException;
044: import java.io.UnsupportedEncodingException;
045: import java.util.ArrayList;
046: import java.util.Enumeration;
047: import java.util.List;
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.StreamUtils;
053: import org.netbeans.installer.utils.StringUtils;
054: import org.netbeans.installer.utils.helper.JavaCompatibleProperties;
055: import org.netbeans.installer.utils.system.launchers.LauncherProperties;
056: import org.netbeans.installer.utils.system.launchers.LauncherResource;
057: import org.netbeans.installer.utils.progress.Progress;
058: import org.netbeans.installer.utils.system.NativeUtils;
059:
060: /**
061: *
062: * @author Dmitry Lipin
063: */
064: public class ShLauncher extends CommonLauncher {
065: public static final String SH_LAUNCHER_STUB_NAME = "launcher.sh"; //NOI18N
066: public static final String DEFAULT_UNIX_RESOURCE_SUFFIX = NativeUtils.NATIVE_LAUNCHER_RESOURCE_SUFFIX
067: + "unix/"; //NOI18N
068: public static final String I18N = "i18n"; //NOI18N
069: public static final String SH_LAUNCHER_STUB = DEFAULT_UNIX_RESOURCE_SUFFIX
070: + SH_LAUNCHER_STUB_NAME;
071:
072: private static final String SH_EXT = ".sh"; //NOI18N
073: private static final int SH_BLOCK = 1024;
074: private static final String SH_INDENT = " "; //NOI18N
075: private static final String SH_LINE_SEPARATOR = StringUtils.LF;
076: private static final String SH_COMMENT = "#";
077:
078: /**
079: * Minimal supported Java version for the launcher.<br><br>
080: * Due to the URLConnection issues with "spaced" names set this value to 1.5.0_03
081: * as these issues were fixed somewhere between Update 1 and Update 3. <br>
082: * Related issues:
083: * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5103449">5103449</a>
084: * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6181108">6181108</a>
085: * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6227551">6227551</a>
086: * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6219199">6219199</a>
087: * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4979820">4979820</a>
088: */
089: public static final String MIN_JAVA_VERSION_UNIX = "1.5.0_03";
090:
091: private static final String[] JAVA_COMMON_LOCATIONS = {
092: "/usr/java", "/usr/java/*", "/usr/jdk", "/usr/jdk/*",
093: "/usr/j2se", "/usr/j2se/*", "/usr/j2sdk", "/usr/j2sdk/*",
094:
095: "/usr/java/jdk", "/usr/java/jdk/*", "/usr/jdk/instances",
096: "/usr/jdk/instances/*",
097:
098: "/usr/local/java", "/usr/local/java/*", "/usr/local/jdk",
099: "/usr/local/jdk/*", "/usr/local/j2se", "/usr/local/j2se/*",
100: "/usr/local/j2sdk", "/usr/local/j2sdk/*",
101:
102: "/opt/java", "/opt/java/*", "/opt/jdk*", "/opt/jdk/*",
103: "/opt/j2sdk", "/opt/j2sdk/*", "/opt/j2se", "/opt/j2se/*",
104:
105: "/usr/lib/jvm", "/usr/lib/jvm/*", "/usr/lib/jdk*",
106:
107: "/export/jdk", "/export/jdk/*", "/export/java",
108: "/export/java/*", "/export/j2se", "/export/j2se/*",
109: "/export/j2sdk", "/export/j2sdk/*" };
110:
111: public ShLauncher(LauncherProperties props) {
112: super (props);
113: }
114:
115: public void initialize() throws IOException {
116: LogManager.log("Checking SH launcher parameters..."); // NOI18N
117: checkAllParameters();
118: }
119:
120: public File create(Progress progress) throws IOException {
121:
122: FileOutputStream fos = null;
123: try {
124:
125: progress.setPercentage(Progress.START);
126: long total = getBundledFilesSize();
127: fos = new FileOutputStream(outputFile, false);
128:
129: StringBuilder sb = new StringBuilder(getStubString());
130:
131: addShInitialComment(sb);
132: addPossibleJavaLocations(sb);
133: addI18NStrings(sb);
134: addTestJVMFile(sb);
135: addClasspathJars(sb);
136: addJavaCompatible(sb);
137: addOtherResources(sb);
138: addNumberVariable(sb, "TOTAL_BUNDLED_FILES_SIZE",
139: getBundledFilesSize());
140: addNumberVariable(sb, "TOTAL_BUNDLED_FILES_NUMBER",
141: getBundledFilesNumber());
142:
143: LogManager.log("Main Class : " + mainClass);
144: addStringVariable(sb, "MAIN_CLASS", mainClass);
145:
146: LogManager.log("TestJVM Class : " + testJVMClass);
147: addStringVariable(sb, "TEST_JVM_CLASS", testJVMClass);
148:
149: addNumberVariable(sb, "JVM_ARGUMENTS_NUMBER", jvmArguments
150: .size());
151: int counter = 0;
152: for (String arg : jvmArguments) {
153: addStringVariable(sb, "JVM_ARGUMENT_" + (counter),
154: escapeVarSign(escapeSlashes(arg)));
155: LogManager.log("... jvm argument [" + counter + "] = "
156: + arg);
157: counter++;
158: }
159:
160: addNumberVariable(sb, "APP_ARGUMENTS_NUMBER", appArguments
161: .size());
162: counter = 0;
163: for (String arg : appArguments) {
164: addStringVariable(sb, "APP_ARGUMENT_" + (counter),
165: escapeVarSign(escapeSlashes(arg)));
166: LogManager.log("... app argument [" + counter + "] = "
167: + arg);
168: counter++;
169: }
170:
171: String token = "_^_^_^_^_^_^_^_^"; // max size: (10^16-1) bytes
172:
173: sb
174: .append("LAUNCHER_STUB_SIZE=" + token
175: + SH_LINE_SEPARATOR);
176:
177: sb.append("entryPoint \"$@\"" + SH_LINE_SEPARATOR);
178: nextLine(sb);
179:
180: long size = sb.length();
181:
182: long fullBlocks = (size - (size % SH_BLOCK)) / SH_BLOCK + 1;
183:
184: String str = Long.toString(fullBlocks);
185: int spaces = token.length() - str.length();
186:
187: for (int j = 0; j < spaces; j++) {
188: str += StringUtils.SPACE;
189: }
190:
191: sb.replace(sb.indexOf(token), sb.indexOf(token)
192: + token.length(), str);
193:
194: long pads = fullBlocks * SH_BLOCK - size;
195:
196: for (long i = 0; i < pads; i++) {
197: sb.append(SH_COMMENT);
198: }
199: addStringBuilder(fos, sb, false);
200: addBundledData(fos, progress, total);
201: } catch (IOException ex) {
202: LogManager.log(ex);
203: try {
204: fos.close();
205: } catch (IOException e) {
206: LogManager.log(e);
207: }
208:
209: try {
210: FileUtils.deleteFile(outputFile);
211: } catch (IOException e) {
212: LogManager.log(e);
213: }
214: fos = null;
215: } finally {
216:
217: if (fos != null) {
218: try {
219: fos.close();
220: } catch (IOException ex) {
221: LogManager.log(ex);
222: throw ex;
223: }
224: }
225: progress.setPercentage(Progress.COMPLETE);
226: }
227: return outputFile;
228: }
229:
230: public String[] getExecutionCommand() {
231: return new String[] { outputFile.getAbsolutePath() };
232: }
233:
234: public List<JavaCompatibleProperties> getDefaultCompatibleJava() {
235: List<JavaCompatibleProperties> list = new ArrayList<JavaCompatibleProperties>();
236: list.add(new JavaCompatibleProperties(MIN_JAVA_VERSION_UNIX,
237: null, null, null, null));
238: return list;
239: }
240:
241: protected void addOtherResources(StringBuilder sb)
242: throws IOException {
243: int counter = 0;
244: addNumberVariable(sb, "OTHER_RESOURCES_NUMBER", otherResources
245: .size());
246:
247: for (LauncherResource resource : otherResources) {
248: addLauncherResource(sb, resource, "OTHER_RESOURCE_"
249: + counter);
250: counter++;
251: }
252:
253: }
254:
255: private void addLauncherResource(StringBuilder sb,
256: LauncherResource resource, String id) throws IOException {
257: long type = resource.getPathType().toLong();
258: addNumberVariable(sb, id + "_TYPE", type); //NOI18N
259:
260: String path;
261: if (resource.isBundled()) {
262: long size = resource.getSize();
263:
264: if (resource.isBasedOnResource()) {
265: path = resource.getPathType().getPathString(
266: ResourceUtils.getResourceFileName(resource
267: .getPath()));
268: } else {
269: path = resource.getAbsolutePath();
270: }
271: addNumberVariable(sb, id + "_SIZE", size);
272: addStringVariable(sb, id + "_MD5", resource.getMD5());
273: } else {
274: path = resource.getAbsolutePath();
275: }
276:
277: addStringVariable(sb, id + "_PATH",
278: escapeVarSign(escapeSlashesAndChars(path)));
279:
280: }
281:
282: protected String getI18NResourcePrefix() {
283: return DEFAULT_UNIX_RESOURCE_SUFFIX;
284: }
285:
286: public String getExtension() {
287: return SH_EXT;
288: }
289:
290: private String escapeChars(String str) {
291: return (str == null) ? StringUtils.EMPTY_STRING : str.replace(
292: "\n", "\\n").replace("\t", "\\\\t").replace("\r",
293: "\\\\r").replace("`", "\\`").replaceAll("\"", "\\\\\"");
294:
295: }
296:
297: private String escapeSlashesAndChars(String str) {
298: return escapeSlashes(escapeChars(str));
299: }
300:
301: private String escapeVarSign(String str) {
302: return (str == null) ? StringUtils.EMPTY_STRING : str.replace(
303: "$", "\\$");
304: }
305:
306: private String escapeSlashes(String str) {
307: return (str == null) ? StringUtils.EMPTY_STRING : str.replace(
308: StringUtils.BACK_SLASH, StringUtils.DOUBLE_BACK_SLASH);
309: }
310:
311: private String getUTF8(String str,
312: boolean changePropertyCounterStyle)
313: throws UnsupportedEncodingException {
314: if (!changePropertyCounterStyle) {
315: return getUTF8(str);
316: } else {
317: String string = StringUtils.EMPTY_STRING;
318: int maxCounter = 0;
319: while (str.indexOf(getJavaCounter(maxCounter)) != -1) {
320: maxCounter++;
321: }
322: boolean con;
323: String jc;
324: for (int i = 0; i < str.length(); i++) {
325: con = false;
326: for (int j = 0; j < maxCounter; j++) {
327: jc = getJavaCounter(j);
328: if (str.indexOf(jc) == i) {
329: string += "$" + (j + 1);
330: i += jc.length();
331: con = true;
332: break;
333: }
334: }
335: if (!con) {
336: string += getUTF8(str.substring(i, i + 1));
337: }
338: }
339:
340: return string;
341: }
342: }
343:
344: private String getStubString() throws IOException {
345: InputStream stubStream;
346:
347: if (stubFile != null) {
348: checkParameter("stub file", stubFile);
349: stubStream = new FileInputStream(stubFile);
350: } else {
351: stubStream = ResourceUtils.getResource(SH_LAUNCHER_STUB);
352: }
353: CharSequence cs = StreamUtils.readStream(stubStream);
354: stubStream.close();
355:
356: String[] strings = StringUtils.splitByLines(cs);
357: String stubString = StringUtils.asString(strings,
358: SH_LINE_SEPARATOR);
359: return stubString;
360: }
361:
362: private String getUTF8(String str)
363: throws UnsupportedEncodingException {
364: String repr = StringUtils.EMPTY_STRING;
365: for (byte oneByte : str.getBytes(StringUtils.ENCODING_UTF8)) {
366: repr += StringUtils.BACK_SLASH
367: + Integer.toOctalString(256 + oneByte);
368: }
369: return repr;
370: }
371:
372: private String changePropertyCounterStyle(String string) {
373: int counter = 0;
374: String jp;
375: String str = string;
376: do {
377: jp = getJavaCounter(counter);
378: if (str.indexOf(jp) != -1) {
379: str = str.replace(jp, "$" + (counter + 1));
380: } else {
381: break;
382: }
383: counter++;
384: } while (true);
385: return str;
386: }
387:
388: private void addStringVariable(StringBuilder sb, String name,
389: String value) {
390: String str = (value != null) ? value : StringUtils.EMPTY_STRING;
391: sb.append(name + StringUtils.EQUAL + StringUtils.QUOTE + str
392: + StringUtils.QUOTE + SH_LINE_SEPARATOR);
393: }
394:
395: private void addNumberVariable(StringBuilder sb, String name,
396: long value) {
397: sb.append(name + StringUtils.EQUAL + value + SH_LINE_SEPARATOR);
398: }
399:
400: private void nextLine(StringBuilder sb) {
401: sb.append(SH_LINE_SEPARATOR);
402: }
403:
404: private void addJavaCompatible(StringBuilder sb) throws IOException {
405: // add java compatibility properties number
406: nextLine(sb);
407:
408: LogManager.log("Total compatible java properties : "
409: + compatibleJava.size()); //NOI18N
410: addNumberVariable(sb, "JAVA_COMPATIBLE_PROPERTIES_NUMBER",
411: compatibleJava.size());
412:
413: for (int i = 0; i < compatibleJava.size(); i++) {
414: nextLine(sb);
415: sb.append("setJavaCompatibilityProperties_" + i + "() {"
416: + SH_LINE_SEPARATOR);
417:
418: JavaCompatibleProperties prop = compatibleJava.get(i);
419: LogManager.log("... adding compatible jvm [" + i + "] : "
420: + prop.toString()); //NOI18N
421: addStringVariable(sb, "JAVA_COMP_VERSION_MIN", prop
422: .getMinVersion());
423: addStringVariable(sb, "JAVA_COMP_VERSION_MAX", prop
424: .getMaxVersion());
425: addStringVariable(sb, "JAVA_COMP_VENDOR", prop.getVendor());
426: addStringVariable(sb, "JAVA_COMP_OSNAME", prop.getOsName());
427: addStringVariable(sb, "JAVA_COMP_OSARCH", prop.getOsArch());
428: sb.append("}");
429: nextLine(sb);
430: }
431: }
432:
433: private void addTestJVMFile(StringBuilder sb) throws IOException {
434: nextLine(sb);
435: addLauncherResource(sb, testJVMFile, "TEST_JVM_FILE");
436: }
437:
438: private void addClasspathJars(StringBuilder sb) throws IOException {
439: nextLine(sb);
440:
441: addNumberVariable(sb, "JARS_NUMBER", jars.size()); //NOI18N
442:
443: int counter = 0;
444: for (LauncherResource jarFile : jars) {
445: addLauncherResource(sb, jarFile, "JAR_" + counter);
446:
447: counter++;
448: }
449: nextLine(sb);
450: }
451:
452: private void addI18NStrings(StringBuilder sb) throws IOException {
453: Object[] locales = i18nMap.keySet().toArray();
454: addNumberVariable(sb, "LAUNCHER_LOCALES_NUMBER", locales.length); //NOI18N
455:
456: for (int i = 0; i < locales.length; i++) {
457: addStringVariable(sb, "LAUNCHER_LOCALE_NAME_" + i, //NOI18N
458: locales[i].toString());
459: }
460:
461: nextLine(sb);
462:
463: for (int i = 0; i < locales.length; i++) {
464: String locale = locales[i].toString();
465: sb.append("getLocalizedMessage_" + locale + "() {"
466: + SH_LINE_SEPARATOR);
467: sb.append(SH_INDENT + "arg=$1" + SH_LINE_SEPARATOR);
468: sb.append(SH_INDENT + "shift" + SH_LINE_SEPARATOR);
469: sb.append(SH_INDENT + "case $arg in" + SH_LINE_SEPARATOR);
470: PropertyResourceBundle rb = i18nMap.get(locales[i]);
471: Enumeration<String> en = rb.getKeys();
472: while (en.hasMoreElements()) {
473: String name = en.nextElement();
474: String value = rb.getString(name);
475: sb.append(SH_INDENT + "\"" + name + "\")"
476: + SH_LINE_SEPARATOR);
477: String printString = value;
478: if (locale == null || locale.equals("")) {
479: printString = escapeChars(changePropertyCounterStyle(printString));
480: } else {
481: printString = getUTF8(printString, true);
482: }
483: sb.append(SH_INDENT + SH_INDENT + "printf \""
484: + printString + "\\n" + "\""
485: + SH_LINE_SEPARATOR);
486: sb.append(SH_INDENT + SH_INDENT + ";;"
487: + SH_LINE_SEPARATOR);
488:
489: }
490: sb.append(SH_INDENT + "*)" + SH_LINE_SEPARATOR);
491: sb.append(SH_INDENT + SH_INDENT + "printf \"$arg\\n\""
492: + SH_LINE_SEPARATOR);
493: sb.append(SH_INDENT + SH_INDENT + ";;" + SH_LINE_SEPARATOR);
494: sb.append(SH_INDENT + "esac" + SH_LINE_SEPARATOR);
495: sb.append("}" + SH_LINE_SEPARATOR);
496: nextLine(sb);
497: }
498:
499: }
500:
501: private void addShInitialComment(StringBuilder sb)
502: throws IOException {
503: nextLine(sb);
504: nextLine(sb);
505: for (int i = 0; i < 80; i++) {
506: sb.append(SH_COMMENT);
507: }
508: nextLine(sb);
509: sb.append(SH_COMMENT + " Added by the bundle builder" + //NOI18N
510: SH_LINE_SEPARATOR);
511: addNumberVariable(sb, "FILE_BLOCK_SIZE", SH_BLOCK);//NOI18N
512: nextLine(sb);
513: }
514:
515: private int addJavaPaths(int count, StringBuilder sb,
516: List<LauncherResource> list) throws IOException {
517: int counter = count;
518: for (LauncherResource location : list) {
519: addLauncherResource(sb, location, "JAVA_LOCATION_"
520: + counter);
521: counter++;
522: }
523: return counter;
524: }
525:
526: private int addJavaPaths(int count, StringBuilder sb, String[] paths)
527: throws IOException {
528: List<LauncherResource> list = new ArrayList<LauncherResource>();
529: for (String path : paths) {
530: list.add(new LauncherResource(
531: LauncherResource.Type.ABSOLUTE, path));
532: }
533: return addJavaPaths(count, sb, list);
534: }
535:
536: protected String[] getCommonSystemJavaLocations() {
537: return JAVA_COMMON_LOCATIONS;
538: }
539:
540: private void addPossibleJavaLocations(StringBuilder sb)
541: throws IOException {
542: int total = 0;
543: total = addJavaPaths(total, sb, jvms);
544: total = addJavaPaths(total, sb, getCommonSystemJavaLocations());
545: addNumberVariable(sb, "JAVA_LOCATION_NUMBER", total); //NOI18N
546: nextLine(sb);
547: }
548:
549: private void fillWithPads(FileOutputStream fos, long sz)
550: throws IOException {
551: long d = (SH_BLOCK - (sz % SH_BLOCK)) % SH_BLOCK;
552: for (int i = 0; i < d; i++) {
553: addString(fos, SH_LINE_SEPARATOR, false);
554: }
555: }
556:
557: private void addLauncherResourceData(FileOutputStream fos,
558: LauncherResource resource, Progress progress, long total)
559: throws IOException {
560: if (resource.isBundled()) { // if bundle TestJVM
561: LogManager.log("Bundle testJVM file..."); //NOI18N
562: InputStream is = null;
563: try {
564: String path = resource.getPath();
565: LogManager.log("... path is " + path); //NOI18N
566: is = resource.getInputStream();
567: addData(fos, is, progress, total);
568: fillWithPads(fos, resource.getSize());
569: } finally {
570: try {
571: if (is != null) {
572: is.close();
573: }
574: } catch (IOException ex) {
575: LogManager.log(ex);
576: }
577: }
578: LogManager.log("... done bundle launcher resource file");//NOI18N
579: }
580: }
581:
582: private void addBundledData(FileOutputStream fos,
583: Progress progress, long total) throws IOException {
584: addLauncherResourceData(fos, testJVMFile, progress, total);
585:
586: for (LauncherResource jvm : jvms) {
587: addLauncherResourceData(fos, jvm, progress, total);
588: }
589: for (LauncherResource jar : jars) {
590: addLauncherResourceData(fos, jar, progress, total);
591: }
592: for (LauncherResource other : otherResources) {
593: addLauncherResourceData(fos, other, progress, total);
594: }
595: }
596: }
|