blob: 1b8698e97111cdf3dc600bdfa69792ecb8c9a196 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.installer.utils.system.launchers.impl;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.PropertyResourceBundle;
import org.netbeans.installer.utils.FileUtils;
import org.netbeans.installer.utils.LogManager;
import org.netbeans.installer.utils.ResourceUtils;
import org.netbeans.installer.utils.StreamUtils;
import org.netbeans.installer.utils.StringUtils;
import org.netbeans.installer.utils.helper.JavaCompatibleProperties;
import org.netbeans.installer.utils.helper.Version;
import org.netbeans.installer.utils.system.launchers.LauncherProperties;
import org.netbeans.installer.utils.system.launchers.LauncherResource;
import org.netbeans.installer.utils.progress.Progress;
import org.netbeans.installer.utils.system.NativeUtils;
/**
*
* @author Dmitry Lipin
*/
public class ShLauncher extends CommonLauncher {
public static final String SH_LAUNCHER_STUB_NAME = "launcher.sh"; //NOI18N
public static final String DEFAULT_UNIX_RESOURCE_SUFFIX =
NativeUtils.NATIVE_LAUNCHER_RESOURCE_SUFFIX +
"unix/"; //NOI18N
public static final String I18N = "i18n"; //NOI18N
public static final String SH_LAUNCHER_STUB =
DEFAULT_UNIX_RESOURCE_SUFFIX + SH_LAUNCHER_STUB_NAME;
public static final String DEFAULT_UNIX_RESOURCE_I18N =
DEFAULT_UNIX_RESOURCE_SUFFIX + I18N + "/"; //NOI18N
public static final String DEFAULT_UNIX_RESOURCE_I18N_BUNDLE_NAME =
"launcher"; //NOI18N
private static final String SH_EXT = ".sh"; //NOI18N
private static final int SH_BLOCK = 1024;
private static final String SH_INDENT = " "; //NOI18N
private static final String SH_LINE_SEPARATOR = StringUtils.LF;
private static final String SH_COMMENT = "#";
/**
* Minimal supported Java version for the launcher.<br><br>
* Due to the URLConnection issues with "spaced" names set this value to 1.5.0_03
* as these issues were fixed somewhere between Update 1 and Update 3. <br>
* Related issues:
* <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5103449">5103449</a>
* <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6181108">6181108</a>
* <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6227551">6227551</a>
* <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6219199">6219199</a>
* <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4979820">4979820</a>
*/
public static final String MIN_JAVA_VERSION_UNIX = "1.5.0_03";
/* IBM does not report the update number so allow to work even on 1.5.0*/
public static final String MIN_IBM_JAVA_VERSION = "1.5.0";
private static final String [] JAVA_COMMON_LOCATIONS = {
"/usr/java*", "/usr/java/*",
"/usr/jdk*", "/usr/jdk/*",
"/usr/j2se", "/usr/j2se/*",
"/usr/j2sdk", "/usr/j2sdk/*",
"/usr/java/jdk*", "/usr/java/jdk/*",
"/usr/jdk/instances", "/usr/jdk/instances/*",
"/usr/local/java", "/usr/local/java/*",
"/usr/local/jdk*", "/usr/local/jdk/*",
"/usr/local/j2se", "/usr/local/j2se/*",
"/usr/local/j2sdk","/usr/local/j2sdk/*",
"/opt/java*", "/opt/java/*",
"/opt/jdk*", "/opt/jdk/*",
"/opt/j2sdk", "/opt/j2sdk/*",
"/opt/j2se", "/opt/j2se/*",
"/usr/lib/jvm",
"/usr/lib/jvm/*",
"/usr/lib/jdk*",
"/export/jdk*", "/export/jdk/*",
"/export/java", "/export/java/*",
"/export/j2se", "/export/j2se/*",
"/export/j2sdk", "/export/j2sdk/*"
};
public ShLauncher(LauncherProperties props) {
super(props);
}
public void initialize() throws IOException {
LogManager.log("Checking SH launcher parameters..."); // NOI18N
checkAllParameters();
}
public File create( Progress progress) throws IOException {
FileOutputStream fos = null;
try {
progress.setPercentage(Progress.START);
long total = getBundledFilesSize();
fos = new FileOutputStream(outputFile,false);
StringBuilder sb = new StringBuilder(getStubString());
addShInitialComment(sb);
addPossibleJavaLocations(sb);
addI18NStrings(sb);
addTestJVMFile(sb);
addClasspathJars(sb);
addJavaCompatible(sb);
addOtherResources(sb);
addNumberVariable(sb, "TOTAL_BUNDLED_FILES_SIZE", getBundledFilesSize());
addNumberVariable(sb, "TOTAL_BUNDLED_FILES_NUMBER", getBundledFilesNumber());
LogManager.log("Main Class : " + mainClass);
addStringVariable(sb, "MAIN_CLASS", mainClass);
LogManager.log("TestJVM Class : " + testJVMClass);
addStringVariable(sb, "TEST_JVM_CLASS", testJVMClass);
addNumberVariable(sb, "JVM_ARGUMENTS_NUMBER", jvmArguments.size());
int counter = 0;
for(String arg : jvmArguments) {
addStringVariable(sb, "JVM_ARGUMENT_" + (counter),
escapeVarSign(escapeSlashes(arg)));
LogManager.log("... jvm argument [" + counter + "] = " + arg);
counter++;
}
addNumberVariable(sb, "APP_ARGUMENTS_NUMBER", appArguments.size());
counter = 0;
for(String arg : appArguments) {
addStringVariable(sb, "APP_ARGUMENT_" + (counter),
escapeVarSign(escapeSlashes(arg)));
LogManager.log("... app argument [" + counter + "] = " + arg);
counter++;
}
String token = "_^_^_^_^_^_^_^_^"; // max size: (10^16-1) bytes
sb.append("LAUNCHER_STUB_SIZE=" + token + SH_LINE_SEPARATOR);
sb.append("entryPoint \"$@\"" + SH_LINE_SEPARATOR);
nextLine(sb);
long size = sb.length();
long fullBlocks = (size - (size % SH_BLOCK)) / SH_BLOCK + 1;
String str = Long.toString(fullBlocks);
int spaces = token.length() - str.length();
for(int j=0;j < spaces; j++) {
str+= StringUtils.SPACE;
}
sb.replace(sb.indexOf(token), sb.indexOf(token) + token.length(), str);
long pads = fullBlocks * SH_BLOCK - size;
for ( long i=0; i < pads;i++) {
sb.append(SH_COMMENT);
}
addStringBuilder(fos,sb,false);
addBundledData(fos, progress, total);
} catch (IOException ex) {
LogManager.log(ex);
try {
fos.close();
} catch (IOException e) {
LogManager.log(e);
}
try {
FileUtils.deleteFile(outputFile);
} catch (IOException e) {
LogManager.log(e);
}
fos = null;
} finally {
if(fos!=null) {
try {
fos.close();
} catch (IOException ex) {
LogManager.log(ex);
throw ex;
}
}
progress.setPercentage(Progress.COMPLETE);
}
return outputFile;
}
public String[] getExecutionCommand() {
return new String [] {outputFile.getAbsolutePath()};
}
@Override
public List <JavaCompatibleProperties> getDefaultCompatibleJava(Version version) {
if (version.equals(Version.getVersion("1.5"))) {
List <JavaCompatibleProperties> list = new ArrayList <JavaCompatibleProperties>();
list.add(new JavaCompatibleProperties(
MIN_JAVA_VERSION_UNIX, null, null, null, null));
list.add(new JavaCompatibleProperties(
MIN_IBM_JAVA_VERSION, null, "IBM Corporation", null, null));
return list;
} else {
return super.getDefaultCompatibleJava(version);
}
}
protected void addOtherResources(StringBuilder sb) throws IOException {
int counter = 0;
addNumberVariable(sb, "OTHER_RESOURCES_NUMBER", otherResources.size());
for(LauncherResource resource : otherResources) {
addLauncherResource(sb, resource, "OTHER_RESOURCE_" + counter);
counter ++;
}
}
private void addLauncherResource(StringBuilder sb, LauncherResource resource, String id) throws IOException {
long type = resource.getPathType().toLong();
addNumberVariable(sb, id + "_TYPE", type); //NOI18N
String path;
if(resource.isBundled()) {
long size = resource.getSize();
if(resource.isBasedOnResource()) {
path = resource.getPathType().getPathString(
ResourceUtils.getResourceFileName(resource.getPath()));
} else {
path = resource.getAbsolutePath();
}
addNumberVariable(sb, id + "_SIZE", size);
addStringVariable(sb, id + "_MD5", resource.getMD5());
} else {
path = resource.getAbsolutePath();
}
addStringVariable(sb, id + "_PATH",
escapeVarSign(escapeSlashesAndChars(path)));
}
@Override
public String getI18NResourcePrefix() {
return i18nPrefix != null ? i18nPrefix :
DEFAULT_UNIX_RESOURCE_I18N;
}
@Override
public String getI18NBundleBaseName() {
return i18nBundleBaseName != null ? i18nBundleBaseName :
DEFAULT_UNIX_RESOURCE_I18N_BUNDLE_NAME;
}
public String getExtension() {
return SH_EXT;
}
private String escapeChars(String str) {
return (str==null) ? StringUtils.EMPTY_STRING :
str.replace("\n","\\n").
replace("\t","\\\\t").
replace("\r","\\\\r").
replace("`","\\`").
replaceAll("\"","\\\\\"");
}
private String escapeSlashesAndChars(String str) {
return escapeSlashes(escapeChars(str));
}
private String escapeVarSign(String str) {
return (str==null) ? StringUtils.EMPTY_STRING :
str.replace("$", "\\$");
}
private String escapeSlashes(String str) {
return (str==null) ? StringUtils.EMPTY_STRING :
str.replace(StringUtils.BACK_SLASH,
StringUtils.DOUBLE_BACK_SLASH);
}
private String getUTF8(String str, boolean changePropertyCounterStyle) throws UnsupportedEncodingException{
if(!changePropertyCounterStyle) {
return getUTF8(str);
} else {
String string = StringUtils.EMPTY_STRING;
int maxCounter=0;
while(str.indexOf(getJavaCounter(maxCounter))!=-1) {
maxCounter++;
}
boolean con;
String jc;
for(int i=0;i<str.length();i++) {
con = false;
for(int j=0;j<maxCounter;j++) {
jc = getJavaCounter(j);
if(str.indexOf(jc)== i) {
string += "$" + (j + 1);
i+=jc.length();
con = true;
break;
}
}
if(!con) {
string+=getUTF8(str.substring(i,i+1));
}
}
return string;
}
}
private String getStubString() throws IOException {
InputStream stubStream;
if(stubFile!=null) {
checkParameter("stub file", stubFile);
stubStream = new FileInputStream(stubFile);
} else {
stubStream = ResourceUtils.getResource(SH_LAUNCHER_STUB);
}
CharSequence cs = StreamUtils.readStream(stubStream);
stubStream.close();
String [] strings = StringUtils.splitByLines(cs);
String stubString = StringUtils.asString(strings, SH_LINE_SEPARATOR);
return stubString;
}
private String getUTF8(String str) throws UnsupportedEncodingException{
String repr = StringUtils.EMPTY_STRING;
for(byte oneByte : str.getBytes(StringUtils.ENCODING_UTF8)) {
repr+= StringUtils.BACK_SLASH + Integer.toOctalString(256 + oneByte);
}
return repr;
}
private String changePropertyCounterStyle(String string) {
int counter = 0;
String jp;
String str = string;
do {
jp = getJavaCounter(counter);
if(str.indexOf(jp)!=-1) {
str = str.replace(jp, "$" + (counter + 1) );
} else {
break;
}
counter++;
}
while (true);
return str;
}
private void addVersionVariable(StringBuilder sb, String name, Version version) {
String str = (version != null) ? version.toJdkStyle() : StringUtils.EMPTY_STRING;
sb.append(name + StringUtils.EQUAL + StringUtils.QUOTE +
str + StringUtils.QUOTE + SH_LINE_SEPARATOR);
}
private void addStringVariable(StringBuilder sb, String name, String value) {
String str = (value != null) ? value : StringUtils.EMPTY_STRING;
sb.append(name + StringUtils.EQUAL + StringUtils.QUOTE +
str + StringUtils.QUOTE + SH_LINE_SEPARATOR);
}
private void addNumberVariable(StringBuilder sb, String name, long value) {
sb.append(name + StringUtils.EQUAL + value + SH_LINE_SEPARATOR);
}
private void nextLine(StringBuilder sb) {
sb.append(SH_LINE_SEPARATOR);
}
private void addJavaCompatible(StringBuilder sb) throws IOException {
// add java compatibility properties number
nextLine(sb);
LogManager.log("Total compatible java properties : " + compatibleJava.size()); //NOI18N
addNumberVariable(sb, "JAVA_COMPATIBLE_PROPERTIES_NUMBER", compatibleJava.size());
for(int i=0;i<compatibleJava.size();i++) {
nextLine(sb);
sb.append("setJavaCompatibilityProperties_" + i + "() {" + SH_LINE_SEPARATOR);
JavaCompatibleProperties prop = compatibleJava.get(i);
LogManager.log("... adding compatible jvm [" + i + "] : " + prop.toString()); //NOI18N
addVersionVariable(sb, "JAVA_COMP_VERSION_MIN", prop.getMinVersion());
addVersionVariable(sb, "JAVA_COMP_VERSION_MAX", prop.getMaxVersion());
addStringVariable(sb, "JAVA_COMP_VENDOR", prop.getVendor());
addStringVariable(sb, "JAVA_COMP_OSNAME", prop.getOsName());
addStringVariable(sb, "JAVA_COMP_OSARCH", prop.getOsArch());
sb.append("}");
nextLine(sb);
}
}
private void addTestJVMFile(StringBuilder sb) throws IOException {
nextLine(sb);
addLauncherResource(sb, testJVMFile, "TEST_JVM_FILE");
}
private void addClasspathJars(StringBuilder sb) throws IOException {
nextLine(sb);
addNumberVariable(sb, "JARS_NUMBER", jars.size()); //NOI18N
int counter = 0;
for(LauncherResource jarFile : jars) {
addLauncherResource(sb, jarFile, "JAR_" + counter);
counter++;
}
nextLine(sb);
}
private void addI18NStrings(StringBuilder sb) throws IOException {
Object [] locales = i18nMap.keySet().toArray();
addNumberVariable(sb,"LAUNCHER_LOCALES_NUMBER",locales.length); //NOI18N
for(int i=0;i<locales.length;i++) {
addStringVariable(sb,"LAUNCHER_LOCALE_NAME_" + i, //NOI18N
locales[i].toString());
}
nextLine(sb);
for(int i=0;i<locales.length;i++) {
String locale = locales[i].toString();
sb.append("getLocalizedMessage_" + locale + "() {" + SH_LINE_SEPARATOR );
sb.append(SH_INDENT + "arg=$1" + SH_LINE_SEPARATOR );
sb.append(SH_INDENT + "shift" + SH_LINE_SEPARATOR );
sb.append(SH_INDENT + "case $arg in" + SH_LINE_SEPARATOR );
PropertyResourceBundle rb = i18nMap.get(locales[i]);
Enumeration <String>en = rb.getKeys();
while(en.hasMoreElements()) {
String name = en.nextElement();
String value = rb.getString(name);
sb.append(SH_INDENT + "\"" + name + "\")" + SH_LINE_SEPARATOR);
String printString = value;
if(Arrays.equals(printString.getBytes("ISO-8859-1"), printString.getBytes("UTF-8"))) {
printString = escapeChars(changePropertyCounterStyle(printString));
} else {
printString = getUTF8(printString, true);
}
sb.append(SH_INDENT + SH_INDENT + "printf \"" + printString + "\\n" + "\"" + SH_LINE_SEPARATOR);
sb.append(SH_INDENT + SH_INDENT + ";;" + SH_LINE_SEPARATOR);
}
sb.append(SH_INDENT + "*)" + SH_LINE_SEPARATOR);
sb.append(SH_INDENT + SH_INDENT + "printf \"$arg\\n\"" + SH_LINE_SEPARATOR);
sb.append(SH_INDENT + SH_INDENT + ";;" + SH_LINE_SEPARATOR);
sb.append(SH_INDENT + "esac" + SH_LINE_SEPARATOR);
sb.append("}" + SH_LINE_SEPARATOR);
nextLine(sb);
}
}
private void addShInitialComment(StringBuilder sb) throws IOException {
nextLine(sb);
nextLine(sb);
for(int i=0;i<80;i++) {
sb.append(SH_COMMENT);
}
nextLine(sb);
sb.append(SH_COMMENT + " Added by the bundle builder" + //NOI18N
SH_LINE_SEPARATOR);
addNumberVariable(sb,"FILE_BLOCK_SIZE", SH_BLOCK);//NOI18N
nextLine(sb);
}
private int addJavaPaths(int count, StringBuilder sb, List<LauncherResource> list) throws IOException {
int counter = count;
for(LauncherResource location : list) {
addLauncherResource(sb, location, "JAVA_LOCATION_" + counter);
counter ++;
}
return counter;
}
private int addJavaPaths(int count, StringBuilder sb, String [] paths) throws IOException {
List <LauncherResource> list = new ArrayList <LauncherResource> ();
for(String path : paths) {
list.add(new LauncherResource(LauncherResource.Type.ABSOLUTE, path));
}
return addJavaPaths(count, sb, list);
}
protected String [] getCommonSystemJavaLocations() {
return JAVA_COMMON_LOCATIONS;
}
private void addPossibleJavaLocations(StringBuilder sb) throws IOException {
int total = 0;
total = addJavaPaths(total, sb, jvms);
total = addJavaPaths(total, sb, getCommonSystemJavaLocations());
addNumberVariable(sb, "JAVA_LOCATION_NUMBER", total); //NOI18N
nextLine(sb);
}
private void fillWithPads(FileOutputStream fos, long sz) throws IOException {
long d = (SH_BLOCK - (sz % SH_BLOCK)) % SH_BLOCK;
for ( int i=0; i < d; i++) {
addString(fos, SH_LINE_SEPARATOR, false);
}
}
private void addLauncherResourceData(FileOutputStream fos, LauncherResource resource, Progress progress, long total) throws IOException {
if(resource.isBundled()) { // if bundle TestJVM
LogManager.log("Bundle testJVM file..."); //NOI18N
InputStream is = null;
try {
String path = resource.getPath();
LogManager.log("... path is " + path); //NOI18N
is = resource.getInputStream();
addData(fos, is, progress, total);
fillWithPads(fos, resource.getSize());
} finally {
try {
if(is!=null) {
is.close();
}
} catch (IOException ex) {
LogManager.log(ex);
}
}
LogManager.log("... done bundle launcher resource file");//NOI18N
}
}
private void addBundledData(FileOutputStream fos, Progress progress, long total) throws IOException {
addLauncherResourceData(fos, testJVMFile, progress, total);
for(LauncherResource jvm : jvms) {
addLauncherResourceData(fos, jvm,progress,total);
}
for(LauncherResource jar : jars) {
addLauncherResourceData(fos, jar,progress,total);
}
for(LauncherResource other : otherResources) {
addLauncherResourceData(fos, other,progress,total);
}
}
}