/*
* IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
*
* http://izpack.org/
* http://izpack.codehaus.org/
*
* Copyright 2003 Jonathan Halliday
*
* Licensed 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 com.izforge.izpack.installer;
import java.io.File;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import javax.swing.JOptionPane;
import com.izforge.izpack.CustomData;
import com.izforge.izpack.IPSPack;
import com.izforge.izpack.Info;
import com.izforge.izpack.Pack;
import com.izforge.izpack.adaptator.IXMLElement;
import com.izforge.izpack.adaptator.impl.XMLParser;
import com.izforge.izpack.compiler.DynamicVariable;
import com.izforge.izpack.rules.Condition;
import com.izforge.izpack.rules.RulesEngine;
import com.izforge.izpack.util.Debug;
import com.izforge.izpack.util.IoHelper;
import com.izforge.izpack.util.OsConstraint;
import com.izforge.izpack.util.OsVersion;
import com.izforge.izpack.util.VariableSubstitutor;
/**
* Common utility functions for the GUI and text installers. (Do not import swing/awt classes to
* this class.)
*
* @author Jonathan Halliday
* @author Julien Ponge
*/
public class InstallerBase
{
/**
* Resource name of the conditions specification
*/
private static final String CONDITIONS_SPECRESOURCENAME = "conditions.xml";
private RulesEngine rules;
private List<InstallerRequirement> installerrequirements;
private Map<String, List<DynamicVariable>> dynamicvariables;
/**
* The base name of the XML file that specifies the custom langpack. Searched is for the file
* with the name expanded by _ISO3.
*/
protected static final String LANG_FILE_NAME = "CustomLangpack.xml";
public RulesEngine getRules(){
return this.rules;
}
/**
* Loads the installation data. Also sets environment variables to <code>installdata</code>.
* All system properties are available as $SYSTEM_<variable> where <variable> is the actual
* name _BUT_ with all separators replaced by '_'. Properties with null values are never stored.
* Example: $SYSTEM_java_version or $SYSTEM_os_name
*
* @param installdata Where to store the installation data.
* @throws Exception Description of the Exception
*/
public void loadInstallData(AutomatedInstallData installdata) throws Exception
{
// Usefull variables
InputStream in;
ObjectInputStream objIn;
int size;
int i;
// We load the variables
Properties variables = null;
in = InstallerBase.class.getResourceAsStream("/vars");
if (null != in)
{
objIn = new ObjectInputStream(in);
variables = (Properties) objIn.readObject();
objIn.close();
}
// We load the Info data
in = InstallerBase.class.getResourceAsStream("/info");
objIn = new ObjectInputStream(in);
Info inf = (Info) objIn.readObject();
objIn.close();
checkForPrivilegedExecution(inf);
// We put the Info data as variables
installdata.setVariable(ScriptParser.APP_NAME, inf.getAppName());
if (inf.getAppURL() != null)
{
installdata.setVariable(ScriptParser.APP_URL, inf.getAppURL());
}
installdata.setVariable(ScriptParser.APP_VER, inf.getAppVersion());
if (inf.getUninstallerCondition() != null)
{
installdata.setVariable("UNINSTALLER_CONDITION", inf.getUninstallerCondition());
}
// We read the panels order data
in = InstallerBase.class.getResourceAsStream("/panelsOrder");
objIn = new ObjectInputStream(in);
List panelsOrder = (List) objIn.readObject();
objIn.close();
// We read the packs data
in = InstallerBase.class.getResourceAsStream("/packs.info");
objIn = new ObjectInputStream(in);
size = objIn.readInt();
ArrayList availablePacks = new ArrayList();
ArrayList<Pack> allPacks = new ArrayList<Pack>();
for (i = 0; i < size; i++)
{
Pack pk = (Pack) objIn.readObject();
allPacks.add(pk);
if (OsConstraint.oneMatchesCurrentSystem(pk.osConstraints))
{
availablePacks.add(pk);
}
}
objIn.close();
/*
* We read the IPS Packs data
*/
objIn = new ObjectInputStream(
InstallerBase.class.getResourceAsStream("/ips-packs.info"));
installdata.ipsPacks = (List<IPSPack>) objIn.readObject();
objIn.close();
// We determine the operating system and the initial installation path
String dir;
String installPath;
if (OsVersion.IS_WINDOWS)
{
dir = buildWindowsDefaultPath();
}
else if (OsVersion.IS_OSX)
{
dir = "/Applications";
}
else
{
if (new File("/usr/local/").canWrite())
{
dir = "/usr/local";
}
else
{
dir = System.getProperty("user.home");
}
}
// We determine the hostname and IPAdress
String hostname;
String IPAddress;
try
{
InetAddress addr = InetAddress.getLocalHost();
// Get IP Address
IPAddress = addr.getHostAddress();
// Get hostname
hostname = addr.getHostName();
}
catch (Exception e)
{
hostname = "";
IPAddress = "";
}
installdata.setVariable("APPLICATIONS_DEFAULT_ROOT", dir);
dir += File.separator;
installdata.setVariable(ScriptParser.JAVA_HOME, System.getProperty("java.home"));
installdata.setVariable(ScriptParser.CLASS_PATH, System.getProperty("java.class.path"));
installdata.setVariable(ScriptParser.USER_HOME, System.getProperty("user.home"));
installdata.setVariable(ScriptParser.USER_NAME, System.getProperty("user.name"));
installdata.setVariable(ScriptParser.IP_ADDRESS, IPAddress);
installdata.setVariable(ScriptParser.HOST_NAME, hostname);
installdata.setVariable(ScriptParser.FILE_SEPARATOR, File.separator);
Enumeration e = System.getProperties().keys();
while (e.hasMoreElements())
{
String varName = (String) e.nextElement();
String varValue = System.getProperty(varName);
if (varValue != null)
{
varName = varName.replace('.', '_');
installdata.setVariable("SYSTEM_" + varName, varValue);
}
}
if (null != variables)
{
Enumeration enumeration = variables.keys();
String varName;
String varValue;
while (enumeration.hasMoreElements())
{
varName = (String) enumeration.nextElement();
varValue = variables.getProperty(varName);
installdata.setVariable(varName, varValue);
}
}
installdata.info = inf;
installdata.panelsOrder = panelsOrder;
installdata.availablePacks = availablePacks;
installdata.allPacks = allPacks;
// get list of preselected packs
Iterator pack_it = availablePacks.iterator();
while (pack_it.hasNext())
{
Pack pack = (Pack) pack_it.next();
if (pack.preselected)
{
installdata.selectedPacks.add(pack);
}
}
// Set the installation path in a default manner
installPath = dir + inf.getAppName();
if (inf.getInstallationSubPath() != null)
{ // A subpath was defined, use it.
installPath = IoHelper.translatePath(dir + inf.getInstallationSubPath(),
new VariableSubstitutor(installdata.getVariables()));
}
installdata.setInstallPath(installPath);
// Load custom action data.
loadCustomData(installdata);
}
private void checkForPrivilegedExecution(Info info)
{
if (System.getenv("izpack.mode") != null && System.getenv("izpack.mode").equals("privileged"))
{
// We have been launched through a privileged execution, so stop the checkings here!
return;
}
else if (info.isPrivilegedExecutionRequired())
{
final Condition condition = RulesEngine.getCondition(info.getPrivilegedExecutionConditionID());
PrivilegedRunner runner = new PrivilegedRunner(!condition.isTrue());
if (runner.isPlatformSupported() && runner.isElevationNeeded())
{
try
{
if (runner.relaunchWithElevatedRights() == 0)
{
System.exit(0);
}
else
{
throw new RuntimeException("Launching an installer with elevated permissions failed.");
}
}
catch (Exception e)
{
e.printStackTrace();
JOptionPane.showMessageDialog(null, "The installer could not launch itself with administrator permissions.\n" +
"The installation will still continue but you may encounter problems due to insufficient permissions.");
}
}
else if (!runner.isPlatformSupported())
{
JOptionPane.showMessageDialog(null, "This installer should be run by an administrator.\n" +
"The installation will still continue but you may encounter problems due to insufficient permissions.");
}
}
}
/**
* Add the contents of a custom langpack (if exist) to the previos loaded comman langpack. If
* not exist, trace an info and do nothing more.
*
* @param idata install data to be used
*/
protected void addCustomLangpack(AutomatedInstallData idata)
{
// We try to load and add a custom langpack.
try
{
idata.langpack.add(ResourceManager.getInstance().getInputStream(LANG_FILE_NAME));
}
catch (Throwable exception)
{
Debug.trace("No custom langpack available.");
return;
}
Debug.trace("Custom langpack for " + idata.localeISO3 + " available.");
}
/**
* Get the default path for Windows (i.e Program Files/...).
* Windows has a Setting for this in the environment and in the registry.
* Just try to use the setting in the environment. If it fails for whatever reason, we take the former solution (buildWindowsDefaultPathFromProps).
*
* @return The Windows default installation path for applications.
*/
private String buildWindowsDefaultPath()
{
try
{
//get value from environment...
String prgFilesPath = IoHelper.getenv("ProgramFiles");
if (prgFilesPath != null && prgFilesPath.length() > 0)
{
return prgFilesPath;
}
else
{
return buildWindowsDefaultPathFromProps();
}
}
catch (Exception x)
{
x.printStackTrace();
return buildWindowsDefaultPathFromProps();
}
}
/**
* just plain wrong in case the programfiles are not stored where the developer expects them.
* E.g. in custom installations of large companies or if used internationalized version of windows with a language pack.
*
* @return the program files path
*/
private String buildWindowsDefaultPathFromProps()
{
StringBuffer dpath = new StringBuffer("");
try
{
// We load the properties
Properties props = new Properties();
props
.load(InstallerBase.class
.getResourceAsStream("/com/izforge/izpack/installer/win32-defaultpaths.properties"));
// We look for the drive mapping
String drive = System.getProperty("user.home");
if (drive.length() > 3)
{
drive = drive.substring(0, 3);
}
// Now we have it :-)
dpath.append(drive);
// Ensure that we have a trailing backslash (in case drive was
// something
// like "C:")
if (drive.length() == 2)
{
dpath.append("\\");
}
String language = Locale.getDefault().getLanguage();
String country = Locale.getDefault().getCountry();
String language_country = language + "_" + country;
// Try the most specific combination first
if (null != props.getProperty(language_country))
{
dpath.append(props.getProperty(language_country));
}
else if (null != props.getProperty(language))
{
dpath.append(props.getProperty(language));
}
else
{
dpath.append(props.getProperty(Locale.ENGLISH.getLanguage()));
}
}
catch (Exception err)
{
dpath = new StringBuffer("C:\\Program Files");
}
return dpath.toString();
}
/**
* Loads custom data like listener and lib references if exist and fills the installdata.
*
* @param installdata installdata into which the custom action data should be stored
* @throws Exception
*/
private void loadCustomData(AutomatedInstallData installdata) throws Exception
{
// Usefull variables
InputStream in;
ObjectInputStream objIn;
int i;
// Load listeners if exist.
String[] streamNames = AutomatedInstallData.CUSTOM_ACTION_TYPES;
List[] out = new List[streamNames.length];
for (i = 0; i < streamNames.length; ++i)
{
out[i] = new ArrayList();
}
in = InstallerBase.class.getResourceAsStream("/customData");
if (in != null)
{
objIn = new ObjectInputStream(in);
Object listeners = objIn.readObject();
objIn.close();
Iterator keys = ((List) listeners).iterator();
while (keys != null && keys.hasNext())
{
CustomData ca = (CustomData) keys.next();
if (ca.osConstraints != null
&& !OsConstraint.oneMatchesCurrentSystem(ca.osConstraints))
{ // OS constraint defined, but not matched; therefore ignore
// it.
continue;
}
switch (ca.type)
{
case CustomData.INSTALLER_LISTENER:
Class clazz = Class.forName(ca.listenerName);
if (clazz == null)
{
throw new InstallerException("Custom action " + ca.listenerName
+ " not bound!");
}
out[ca.type].add(clazz.newInstance());
break;
case CustomData.UNINSTALLER_LISTENER:
case CustomData.UNINSTALLER_JAR:
out[ca.type].add(ca);
break;
case CustomData.UNINSTALLER_LIB:
out[ca.type].add(ca.contents);
break;
}
}
// Add the current custem action data to the installdata hash map.
for (i = 0; i < streamNames.length; ++i)
{
installdata.customData.put(streamNames[i], out[i]);
}
}
// uninstallerLib list if exist
}
/**
* Reads the conditions specification file and initializes the rules engine.
*/
protected void loadConditions(AutomatedInstallData installdata)
{
// try to load already parsed conditions
try
{
InputStream in = InstallerBase.class.getResourceAsStream("/rules");
ObjectInputStream objIn = new ObjectInputStream(in);
Map rules = (Map) objIn.readObject();
if ((rules != null) && (rules.size() != 0))
{
this.rules = new RulesEngine(rules, installdata);
}
objIn.close();
}
catch (Exception e)
{
Debug.trace("Can not find optional rules");
}
if (rules != null)
{
installdata.setRules(rules);
// rules already read
return;
}
try
{
InputStream input = null;
input = this.getResource(CONDITIONS_SPECRESOURCENAME);
if (input == null)
{
this.rules = new RulesEngine((IXMLElement) null, installdata);
return;
}
XMLParser xmlParser = new XMLParser();
// get the data
IXMLElement conditionsxml = xmlParser.parse(input);
this.rules = new RulesEngine(conditionsxml, installdata);
}
catch (Exception e)
{
Debug.trace("Can not find optional resource " + CONDITIONS_SPECRESOURCENAME);
// there seem to be no conditions
this.rules = new RulesEngine((IXMLElement) null, installdata);
}
installdata.setRules(rules);
}
/**
* Loads Dynamic Variables.
*/
protected void loadDynamicVariables()
{
try
{
InputStream in = InstallerFrame.class.getResourceAsStream("/dynvariables");
ObjectInputStream objIn = new ObjectInputStream(in);
dynamicvariables = (Map<String, List<DynamicVariable>>) objIn.readObject();
objIn.close();
}
catch (Exception e)
{
Debug.trace("Cannot find optional dynamic variables");
System.out.println(e);
}
}
/**
* Load installer conditions
*
* @throws Exception
*/
public void loadInstallerRequirements() throws Exception
{
InputStream in = InstallerBase.class.getResourceAsStream("/installerrequirements");
ObjectInputStream objIn = new ObjectInputStream(in);
this.installerrequirements = (List<InstallerRequirement>) objIn.readObject();
objIn.close();
}
public boolean checkInstallerRequirements(AutomatedInstallData installdata) throws Exception
{
boolean result = true;
for (InstallerRequirement installerrequirement : this.installerrequirements)
{
String conditionid = installerrequirement.getCondition();
Condition condition = RulesEngine.getCondition(conditionid);
if (condition == null)
{
Debug.log(conditionid + " not a valid condition.");
throw new Exception(conditionid + "could not be found as a defined condition");
}
if (!condition.isTrue())
{
String message = installerrequirement.getMessage();
if ((message != null) && (message.length() > 0))
{
String localizedMessage = installdata.langpack.getString(message);
this.showMissingRequirementMessage(localizedMessage);
}
result = false;
break;
}
}
return result;
}
protected void showMissingRequirementMessage(String message){
Debug.log(message);
}
/**
* Gets the stream to a resource.
*
* @param res The resource id.
* @return The resource value, null if not found
* @throws Exception
*/
public InputStream getResource(String res) throws Exception
{
InputStream result;
String basePath = "";
ResourceManager rm = null;
try
{
rm = ResourceManager.getInstance();
basePath = rm.resourceBasePath;
}
catch (Exception e)
{
e.printStackTrace();
}
result = this.getClass().getResourceAsStream(basePath + res);
if (result == null)
{
throw new ResourceNotFoundException("Warning: Resource not found: "
+ res);
}
return result;
}
/**
* Refreshes Dynamic Variables.
*/
protected void refreshDynamicVariables(VariableSubstitutor substitutor, AutomatedInstallData installdata)
{
Debug.log("refreshing dyamic variables.");
if (dynamicvariables != null)
{
for (String dynvarname : dynamicvariables.keySet())
{
Debug.log("Variable: " + dynvarname);
for (DynamicVariable dynvar : dynamicvariables.get(dynvarname))
{
boolean refresh = false;
String conditionid = dynvar.getConditionid();
Debug.log("condition: " + conditionid);
if ((conditionid != null) && (conditionid.length() > 0))
{
if ((rules != null) && rules.isConditionTrue(conditionid))
{
Debug.log("refresh condition");
// condition for this rule is true
refresh = true;
}
}
else
{
Debug.log("refresh condition");
// empty condition
refresh = true;
}
if (refresh)
{
String newvalue = substitutor.substitute(dynvar.getValue(), null);
Debug.log("newvalue: " + newvalue);
installdata.variables.setProperty(dynvar.getName(), newvalue);
}
}
}
}
}
}