blob: f5196609d31519302204f5698f7d31d44e94a028 [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.apache.uima.pear.tools;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.jar.JarFile;
import org.apache.uima.UIMAFramework;
import org.apache.uima.internal.util.I18nUtil;
import org.apache.uima.pear.util.FileUtil;
import org.apache.uima.pear.util.MessageRouter;
import org.apache.uima.pear.util.StringUtil;
import org.apache.uima.resource.PearSpecifier;
import org.apache.uima.util.FileUtils;
import org.xml.sax.SAXException;
/**
* The <code>InstallationController</code> class allows installing PEAR files that contain UIMA
* compliant components. <br />
* <b>Note:</b> current version works both in Windows and Linux.
*
* <br />
* This class may be used in the following ways:
* <ul>
* <li>As a standalone Java application - <br />
* <code>
* java -DUIMA_HOME=%UIMA_HOME% org.apache.uima.pear.tools.InstallationController
* {-local pear_file | component_id} [-root] [installation_directory]
* </code><br />
* where the <code>-local pear_file</code> option allows to install local PEAR file in the local
* file system (without using SITH services); <br />
* the <code>component_id</code> is the ID of the component to be installed using SITH services;
* <br />
* the <code>-root</code> option enables component installation directly in the specified
* installation directory, as opposed to installing component in a <code>component_id</code>
* subdirectory of the specified installation directory; <br />
* the <code>installation_directory</code> is the directory where the new component will be
* installed - if the <code>-root</code> option is specified, the component is installed in this
* directory, otherwise it is installed in a <code>component_id</code> subdirectory of this
* directory; by default - current working directory. </li>
* <li>As a Java object - <br />
* in this case, the caller is expected to set the <code>UIMA_HOME</code> variable, using the
* <code>setUimaHomePath()</code> method, immediately after creating a new instance of the
* <code>InstallationController</code> class. <br />
* Installation is performed by using the <code>installComponent()</code> method. <br />
* Installation verification is performed by using the <code>verifyComponent()</code> method.
* <br />
* Error messages can be retrieved by using the <code>getInstallationMsg()</code> and
* <code>getVerificationMsg()</code> methods. <br />
* <b>Note 1:</b> Starting from version 0.6, the <code>InstallationController</code> class
* utilizes intra-process message routing (see <code>{@link MessageRouter}</code> class).
* Applications need to call the <code>terminate()</code> method on each instance of the
* <code>InstallationController</code> class after all their operations are completed. <br />
* The application can get output and error messages, printed by the
* <code>InstallationController</code>, by adding standard channel listeners (see the
* <code>addMsgListener()</code> method). By default, the output and error messages are printed to
* the standard console streams. Alternatively, the application can use the
* <code>InstallationController</code> constructor that accepts a custom message listener. In this
* case, the output and error messages will not be printed to the standard console streams. <br />
* <b>Note 2:</b> Starting from version 1.4, the <code>InstallationController</code> class
* defines the <code>{@link PackageSelector}</code> interface and allows to plug-in custom package
* selectors for manually or automatically selecting root directories of installed PEAR packages, as
* well as PEAR package files that need to be installed. <br />
* <b>Note 2:</b> Starting from version 1.5, the <code>InstallationController</code> class
* defines the <code>{@link InstallationMonitor}</code> interface and allows to plug-in custom
* installation monitors for reporting component installation status and location of installed
* components. </li>
* </ul>
*
* @see org.apache.uima.pear.tools.InstallationDescriptor
* @see org.apache.uima.pear.tools.InstallationProcessor
* @see org.apache.uima.pear.tools.InstallationTester
* @see org.apache.uima.pear.util.MessageRouter
*/
public class InstallationController {
/**
* The <code>InstallationMonitor</code> interface defines methods required for notifying of
* component installation status and location of the installed PEAR packages.
*/
public static interface InstallationMonitor {
/**
* Notifies of the installation status of a given component. Acceptable status values are
* defined in the <code>InstallationController</code> class.
*
* @param componentId
* The ID of the given component.
* @param status
* Current installation status of the given component. <br />
* Note: Acceptable status values are defined in the
* <code>InstallationController</code> class.
*/
public void setInstallationStatus(String componentId, String status);
/**
* Notifies of the installed PEAR package location for a given component.
*
* @param componentId
* The ID of the given component.
* @param componentRootPath
* The root directory path of the given installed PEAR package in the local file
* system.
*/
public void setInstallationLocation(String componentId, String componentRootPath);
}
/**
* The <code>PackageSelector</code> interface defines methods required for manually or
* automatically selecting installed PEAR package root directories and PEAR package files.
*
*/
public static interface PackageSelector {
/**
* Selects root directory of an installed PEAR package in the local file system. If the given
* component is not installed yet, returns <code>null</code>.
*
* @param componentId
* The ID of the given installed component.
* @return The root directory of the installed PEAR package, or <code>null</code>, if the
* given component is not installed yet.
*/
public File selectPackageDirectory(String componentId);
/**
* Selects a PEAR package file in the local file system. If the given component PEAR file is not
* found, returns <code>null</code>.
*
* @param componentId
* The ID of the given component.
* @return The given PEAR package file, or <code>null</code>, if the PEAR file is not found
* in the local file system.
*/
public File selectPackageFile(String componentId);
/**
* Selects a PEAR package URL in the network. If the given component PEAR package URL is not
* found, returns <code>null</code>.
*
* @param componentId
* The ID of the given component.
* @return The given PEAR package URL, or <code>null</code>, if the PEAR package URL is not
* found.
*/
public URL selectPackageUrl(String componentId);
}
/**
* The <code>TestStatus</code> class encapsulates attributes related to the results of the
* 'serviceability' verification test.
*/
public static class TestStatus {
final public static int TEST_SUCCESSFUL = 0;
final public static int TEST_NOT_SUCCESSFUL = -1;
private int retCode = TEST_NOT_SUCCESSFUL;
private String message = null;
/**
* @return the message
*/
public String getMessage() {
return this.message;
}
/**
* @param message
* the message to set
*/
public void setMessage(String message) {
this.message = message;
}
/**
* @return the retCode
*/
public int getRetCode() {
return this.retCode;
}
/**
* @param retCode
* the retCode to set
*/
public void setRetCode(int retCode) {
this.retCode = retCode;
}
}
// Component installation status values
public static final String INSTALLATION_IN_PROGRESS = "installation_in_progress";
public static final String INSTALLATION_FAILED = "installation_failed";
public static final String INSTALLATION_COMPLETED = "installation_completed";
public static final String VERIFICATION_IN_PROGRESS = "verification_in_progress";
public static final String VERIFICATION_FAILED = "verification_failed";
public static final String VERIFICATION_COMPLETED = "verification_completed";
public static final String VERIFICATION_CANCELLED = "verification_cancelled";
// Verification application class
protected static final String INSTALLATION_TESTER_APP = "org.apache.uima.pear.tools.InstallationTester";
private static final String PEAR_MESSAGE_RESOURCE_BUNDLE = "org.apache.uima.pear.pear_messages";
// Package configuration file
public static final String PACKAGE_CONFIG_FILE = "metadata/PEAR.properties";
// Standard package directories
public static final String PACKAGE_METADATA_DIR = "metadata";
public static final String PACKAGE_BIN_DIR = "bin";
public static final String PACKAGE_CONF_DIR = "conf";
public static final String PACKAGE_DATA_DIR = "data";
public static final String PACKAGE_DESC_DIR = "desc";
public static final String PACKAGE_DOC_DIR = "doc";
public static final String PACKAGE_LIB_DIR = "lib";
public static final String PACKAGE_RESOURCES_DIR = "resources";
public static final String PACKAGE_SOURCES_DIR = "src";
// file generated at the end of local installation
public static final String SET_ENV_FILE = "metadata/setenv.txt";
public static final String PEAR_DESC_FILE_POSTFIX = "_pear.xml";
// UIMA constants
protected static final String UIMA_HOME_ENV = "UIMA_HOME";
// UIMA libraries dirs
protected static final String UIMA_LIB_DIR = "/lib";
protected static final String VINCI_LIB_DIR = "/lib/vinci";
// UIMA libraries file extensions
protected static final String JAR_FILE_EXT = ".jar";
// special environment variables that need to be appended to JVM variables
public static final String CLASSPATH_VAR = "classpath";
protected static final String PATH_VAR = "path";
// command line options
protected static final String LOCAL_OPT = "-local";
protected static final String INSTALL_IN_ROOT_OPT = "-root";
// static attributes
private static boolean __inLocalMode = false;
private static String __osName = null;
// instance attributes
private String _mainComponentId;
private String _installationDirPath;
private File _installationDir;
private boolean _cleanInstallDir = true;
private String _mainComponentRootPath;
private File _mainComponentRoot;
private String _mainPearFileLocation = null;
private Hashtable _installationTable = new Hashtable();
private Hashtable _installationInsDs = new Hashtable();
private InstallationDescriptor _insdObject;
private String _hostIpAddress;
private String _uimaHomePath;
private String _installationMsg;
private String _verificationMsg;
private MessageRouter _msgRouter = null;
private MessageRouter.StdChannelListener _defaultMsgListener = null;
private PackageSelector _packageSelector = null;
private InstallationMonitor _installationMonitor = null;
/**
* Appends a list of JAR files in a given lib directory, separated with the OS dependent separator
* (';' or ':'), to a given initial <code>StringBuffer</code> object. If <code>null</code>
* <code>StringBuffer</code>
* object is specified, creates new <code>StringBuffer</code> object.
*
* @param libDir
* The given lib directory.
* @param listBuffer
* The initial <code>StringBuffer</code> object.
* @return The list of JAR files in the given lib directory, appended to the given
* <code>StringBuffer</code>.
* @throws IOException
* If any I/O exception occurred.
*/
protected static StringBuffer addListOfJarFiles(File libDir, StringBuffer listBuffer)
throws IOException {
// create list of JAR files in a given directory
if (libDir.isDirectory()) {
Collection fileList = FileUtil.createFileList(libDir);
Iterator files = fileList.iterator();
while (files.hasNext()) {
File file = (File) files.next();
if (file.getName().toLowerCase().endsWith(JAR_FILE_EXT)) {
if (listBuffer.length() > 0)
listBuffer.append(File.pathSeparatorChar);
listBuffer.append(file.getAbsolutePath().replace('\\', '/'));
}
}
}
return listBuffer;
}
/**
* Adds a given local environment variable to appropriate system environment variable (before the
* system value). The case of the local environment variable key is ignored.
*
* @param sysEnvTable
* The table of system environment variables.
* @param localKey
* The given local environment variable key.
* @param localValue
* The given local environment variable value.
* @return <code>true</code> if the local value was really added, <code>false</code>
* otherwise.
*/
protected static boolean addToSystemEnvTable(Properties sysEnvTable, String localKey,
String localValue) {
boolean done = false;
Enumeration sysKeys = sysEnvTable.keys();
while (sysKeys.hasMoreElements() && !done) {
String sysKey = (String) sysKeys.nextElement();
// system key and local key could have different cases
if (sysKey.equalsIgnoreCase(localKey)) {
String sysValue = sysEnvTable.getProperty(sysKey);
sysEnvTable.setProperty(sysKey, localValue + File.pathSeparator + sysValue);
done = true;
}
}
return done;
}
/**
* Creates a string array that contains network parameters (in the JVM '-Dname=value' format)
* specified in a given installation descriptor object.
*
* @param insdObject
* The given installation descriptor object.
* @return The string array of network parameters in the JVM format.
*/
public static String[] buildArrayOfNetworkParams(InstallationDescriptor insdObject) {
String[] paramsArray = new String[0];
ArrayList paramsList = new ArrayList();
StringBuffer itemBuffer = new StringBuffer();
Set pNames = insdObject.getMainComponentNetworkParamNames();
// go through specified parameters and add them to the list
if (pNames != null) {
Iterator pList = pNames.iterator();
while (pList.hasNext()) {
String pName = (String) pList.next();
Properties param = insdObject.getMainComponentNetworkParam(pName);
Enumeration keys = param.keys();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
String value = param.getProperty(key);
if (value.length() > 0) {
// add '-Dkey=value' to the list
itemBuffer.setLength(0);
itemBuffer.append("-D");
itemBuffer.append(key.trim());
itemBuffer.append('=');
itemBuffer.append(value.trim());
paramsList.add(itemBuffer.toString());
}
}
}
}
if (paramsList.size() > 0) {
// copy list to array
paramsArray = new String[paramsList.size()];
paramsList.toArray(paramsArray);
}
return paramsArray;
}
/**
* Creates a string that should be added to the CLASSPATH for a given installed component
* associated with a given installation descriptor object.
*
* @param compRootDirPath
* The given root directory of the installed component.
* @param insdObject
* The given installation descriptor object.
* @return The string that should be added to the CLASSPATH for the given component.
* @throws IOException
* If any I/O exception occurred.
*/
public static String buildComponentClassPath(String compRootDirPath,
InstallationDescriptor insdObject) throws IOException {
// create list of JAR files in lib dir.
File compLibDir = new File(compRootDirPath, PACKAGE_LIB_DIR);
StringBuffer cpBuffer = new StringBuffer();
cpBuffer = addListOfJarFiles(compLibDir, cpBuffer);
// append all specified CLASSPATH env.var. settings
Iterator envActions = insdObject.getInstallationActions(
InstallationDescriptor.ActionInfo.SET_ENV_VARIABLE_ACT).iterator();
while (envActions.hasNext()) {
// if env.var.name is CLASSPATH, append value to the buffer
InstallationDescriptor.ActionInfo actInfo = (InstallationDescriptor.ActionInfo) envActions
.next();
if (actInfo.params != null) {
String varName = actInfo.params.getProperty(InstallationDescriptorHandler.VAR_NAME_TAG);
String varValue = actInfo.params.getProperty(InstallationDescriptorHandler.VAR_VALUE_TAG);
if (varName != null && varValue != null && varName.equalsIgnoreCase(CLASSPATH_VAR)) {
if (cpBuffer.length() > 0)
cpBuffer.append(File.pathSeparatorChar);
cpBuffer.append(varValue.replace('\\', '/'));
}
}
}
return cpBuffer.toString();
}
/**
* Creates a string that should be added to the SPATH for a given installed component associated
* with a given installation descriptor object.
*
* @param compRootDirPath
* The given root directory of the installed component.
* @param insdObject
* The given installation descriptor object.
* @return The string that should be added to the SPATH for the given component.
*/
public static String buildComponentPath(String compRootDirPath, InstallationDescriptor insdObject) {
// append 'bin' directory to component path
File compBinDir = new File(compRootDirPath, PACKAGE_BIN_DIR);
StringBuffer pBuffer = new StringBuffer();
if (compBinDir.isDirectory()) {
pBuffer.append(compBinDir.getAbsolutePath().replace('\\', '/'));
}
// append all specified PATH env.var. settings
Iterator envActions = insdObject.getInstallationActions(
InstallationDescriptor.ActionInfo.SET_ENV_VARIABLE_ACT).iterator();
while (envActions.hasNext()) {
// if env.var.name is PATH, append value to the buffer
InstallationDescriptor.ActionInfo actInfo = (InstallationDescriptor.ActionInfo) envActions
.next();
if (actInfo.params != null) {
String varName = actInfo.params.getProperty(InstallationDescriptorHandler.VAR_NAME_TAG);
String varValue = actInfo.params.getProperty(InstallationDescriptorHandler.VAR_VALUE_TAG);
if (varName != null && varValue != null && varName.equalsIgnoreCase(PATH_VAR)) {
if (pBuffer.length() > 0)
pBuffer.append(File.pathSeparatorChar);
pBuffer.append(varValue.replace('\\', '/'));
}
}
}
return pBuffer.toString();
}
/**
* Creates a string that contains the list of environment variables settings (in the JVM
* '-Dname=value' format) included in a given installation descriptor object.
*
* @param insdObject
* The given installation descriptor object.
* @return The string of environment variables settings in the JVM format.
*/
public static String buildListOfEnvVars(InstallationDescriptor insdObject) {
Properties envVarsTable = buildTableOfEnvVars(insdObject);
StringBuffer envBuffer = new StringBuffer();
// add env.var. setting in the JVM format
Enumeration names = envVarsTable.keys();
while (names.hasMoreElements()) {
String varName = (String) names.nextElement();
String varValue = envVarsTable.getProperty(varName);
if (varName.length() > 0 && varValue != null && varValue.length() > 0) {
if (envBuffer.length() > 0)
envBuffer.append(' ');
envBuffer.append("-D");
envBuffer.append(varName);
envBuffer.append('=');
envBuffer.append(varValue);
}
}
return envBuffer.toString();
}
/**
* Creates a string that contains network parameters (in the JVM '-Dname=value' format) specified
* in a given installation descriptor object.
*
* @param insdObject
* The given installation descriptor object.
* @return The string of network parameters in the JVM format.
*/
public static String buildListOfNetworkParams(InstallationDescriptor insdObject) {
StringBuffer paramsBuffer = new StringBuffer();
String[] paramsArray = buildArrayOfNetworkParams(insdObject);
for (int i = 0; i < paramsArray.length; i++) {
// add '-Dkey=value' to the string
paramsBuffer.append(paramsArray[i]);
paramsBuffer.append(' ');
}
return paramsBuffer.toString();
}
/**
* Creates a <code>Properties</code> table that contains (name, value) pairs of environment
* variables settings for a given installation descriptor object.
*
* @param insdObject
* The given installation descriptor object.
* @return The <code>Properties</code> table that contains environment variables settings for
* the given installation descriptor object.
*/
public static Properties buildTableOfEnvVars(InstallationDescriptor insdObject) {
Properties envVarsTable = new Properties();
// find all 'set_env_variable' actions
Iterator envActions = insdObject.getInstallationActions(
InstallationDescriptor.ActionInfo.SET_ENV_VARIABLE_ACT).iterator();
while (envActions.hasNext()) {
// add env.var. settings to the table
InstallationDescriptor.ActionInfo actInfo = (InstallationDescriptor.ActionInfo) envActions
.next();
String varName = actInfo.params.getProperty(InstallationDescriptorHandler.VAR_NAME_TAG);
String varValue = actInfo.params.getProperty(InstallationDescriptorHandler.VAR_VALUE_TAG);
// exclude CLASSPATH and PATH
if (varName != null && varValue != null && !varName.equalsIgnoreCase(CLASSPATH_VAR)
&& !varName.equalsIgnoreCase(PATH_VAR)) {
// check if the same name exists
String curValue = envVarsTable.getProperty(varName);
if (curValue != null) {
// add new value
curValue = curValue + File.pathSeparator + varValue;
envVarsTable.setProperty(varName, curValue);
} else
envVarsTable.setProperty(varName, varValue);
}
}
return envVarsTable;
}
/**
* Creates a string that should be added to the CLASSPATH environment variable for UIMA framework.
*
* @param uimaHomeEnv
* The location of UIMA framework (UIMA_HOME environment variable value).
* @return The CLASSPATH string for UIMA framework.
*/
public static String buildUIMAClassPath(String uimaHomeEnv) {
try {
StringBuffer cpBuffer = new StringBuffer();
File uimaLibDir = new File(uimaHomeEnv + UIMA_LIB_DIR);
cpBuffer = addListOfJarFiles(uimaLibDir, cpBuffer);
File vinciLibDir = new File(uimaHomeEnv + VINCI_LIB_DIR);
cpBuffer = addListOfJarFiles(vinciLibDir, cpBuffer);
return cpBuffer.toString();
} catch (IOException exc) {
return File.pathSeparator;
}
}
/**
* Deletes all installed files for a given component in a given parent directory. If the
* <code>includeDelegates</code> flag is <code>true</code>, deletes also all files installed
* in a given parent directory for separate delegate components, specified in the main
* installation descriptor.
*
* @param componentId
* The given main component ID.
* @param parentDir
* The given parent directory of the main component root directory.
* @param includeDelegates
* Indicates whether files of the specified separate delegate components should be
* deleted.
* @return <code>true</code>, if the deletion operation completed successfully,
* <code>false</code> otherwise.
* @throws IOException
* if any I/O exception occurred.
*/
public static boolean deleteInstalledFiles(String componentId, File parentDir,
boolean includeDelegates) throws IOException {
boolean done = true;
File rootDir = new File(parentDir, componentId);
if (!rootDir.isDirectory()) {
return false;
}
if (includeDelegates) {
// get main installation descriptor
InstallationDescriptorHandler insdHandler = new InstallationDescriptorHandler();
File insdFile = new File(rootDir, InstallationProcessor.INSD_FILE_PATH);
try {
insdHandler.parse(insdFile);
} catch (IOException e) {
throw e;
} catch (Exception err) {
throw new IOException(err.getMessage());
}
InstallationDescriptor insdObject = insdHandler.getInstallationDescriptor();
// get list of separate delegate components
Hashtable dlgComponents = insdObject.getDelegateComponents();
Enumeration dlgCompIds = dlgComponents.keys();
while (dlgCompIds.hasMoreElements()) {
String dlgCompId = (String) dlgCompIds.nextElement();
if (!deleteInstalledFiles(dlgCompId, parentDir, true))
done = false;
}
}
if (!FileUtil.deleteDirectory(rootDir))
done = false;
return done;
}
/**
* Extracts files with a given extension from a given PEAR file into a given target directory. If
* the given filename extension is <code>null</code>, extracts all the files from a given PEAR
* file. Returns the path to the new component root directory.
*
* @param pearFileLocation
* The given PEAR file location.
* @param fileExt
* The given filename extension.
* @param targetDir
* The given target directory.
* @param cleanTarget
* If true, the target directory is cleaned before the PEAR file is installed to it.
* @return The path to the new component root directory.
* @throws IOException
* if any I/O exception occurred.
*/
public static String extractFilesFromPEARFile(String pearFileLocation, String fileExt,
File targetDir, boolean cleanTarget) throws IOException {
return extractFilesFromPEARFile(pearFileLocation, fileExt, targetDir, null, cleanTarget);
}
/**
* Internal implementatiton of the <code>extractFilesFromPEARFile</code> method, which allows
* sending messages to the OUT and ERR queues.
*
* @param componentId
* The given component ID.
* @param pearFileLocation
* The given PEAR file location.
* @param fileExt
* The given filename extension.
* @param targetDir
* The given target directory.
* @param controller
* The instance of the <code>InstallationController<code> class that provides OUT and ERR
* @param cleanTarget
* If true, the target directory is cleaned before the PEAR file is installed to it.
* message routing, or <code>null</code>.
* @return The path to the new component root directory.
* @throws IOException if any I/O exception occurred.
*/
protected static String extractFilesFromPEARFile(String pearFileLocation, String fileExt,
File targetDir, InstallationController controller, boolean cleanTarget)
throws IOException {
// get PEAR file size
long fileSize = FileUtil.getFileSize(pearFileLocation);
// create root directory
if (!targetDir.isDirectory() && !targetDir.mkdirs()) {
//create localized error message
String message = I18nUtil.localizeMessage(PEAR_MESSAGE_RESOURCE_BUNDLE,
"installation_controller_error_creating_install_dir", new Object[] { targetDir
.getAbsolutePath() });
throw new IOException(message);
}
// clean target directory
if (cleanTarget) {
if (FileUtils.deleteRecursive(targetDir)) {
if (!targetDir.mkdirs()) {
//create localized error message
String message = I18nUtil.localizeMessage(PEAR_MESSAGE_RESOURCE_BUNDLE,
"installation_controller_error_creating_install_dir", new Object[] { targetDir
.getAbsolutePath() });
throw new IOException(message);
}
} else {
//create localized error message
String message = I18nUtil.localizeMessage(PEAR_MESSAGE_RESOURCE_BUNDLE,
"installation_controller_error_cleaning_install_dir", new Object[] { targetDir
.getAbsolutePath() });
throw new IOException(message);
}
}
// specify local PEAR file
File pearFile = null;
boolean removeLocalCopy = false;
boolean done = false;
try {
// try to find PEAR file in the local file system
pearFile = new File(pearFileLocation);
if (!pearFile.isFile()) {
// copy PEAR file to the target directory using URL connection
URL pearFileUrl = new URL(pearFileLocation);
if (controller != null) // write message to OUT msg queue
controller.getOutMsgWriter().println(
"[InstallationController]: copying " + fileSize + " bytes from "
+ pearFileUrl.toExternalForm() + " to " + targetDir.getAbsolutePath());
else
// print message to console
System.out.println("[InstallationController]: copying " + fileSize + " bytes from "
+ pearFileUrl.toExternalForm() + " to " + targetDir.getAbsolutePath());
String pearFileName = (new File(pearFileUrl.getFile())).getName();
pearFile = new File(targetDir, pearFileName);
if (!FileUtil.copyFile(pearFileUrl, pearFile))
throw new IOException("cannot copy " + pearFileUrl + " to file "
+ pearFile.getAbsolutePath());
removeLocalCopy = true;
}
if (controller != null) // write message to OUT msg queue
controller.getOutMsgWriter().println(
"[InstallationController]: extracting " + pearFile.getAbsolutePath());
else
// print message to console
System.out.println("[InstallationController]: extracting " + pearFile.getAbsolutePath());
JarFile jarFile = new JarFile(pearFile);
long totalBytes = (fileExt == null) ? FileUtil.extractFilesFromJar(jarFile, targetDir) : // all
// files
FileUtil.extractFilesWithExtFromJar( // files with extension
jarFile, fileExt, targetDir);
if (controller != null) // write message to OUT msg queue
controller.getOutMsgWriter().println(
"[InstallationController]: " + totalBytes + " bytes extracted");
else
// print message to console
System.out.println("[InstallationController]: " + totalBytes + " bytes extracted");
if (removeLocalCopy) {
// remove local copy of PEAR file
if (!pearFile.delete())
pearFile.deleteOnExit();
}
done = true;
} catch (MalformedURLException urlExc) {
throw new FileNotFoundException(pearFileLocation);
} catch (IOException ioExc) {
throw ioExc;
} catch (Throwable err) {
throw new IOException(err.toString());
}
return done ? targetDir.getAbsolutePath() : null;
}
/**
* Extracts all files of a given component from a given PEAR file into a given target directory.
* Returns the path to the new component root directory.
*
* @param pearFileLocation
* The given PEAR file location.
* @param installationDir
* The given target directory.
* @param cleanTarget
* If true, the target directory is cleaned before the PEAR file is installed to it.
* @return The path to the new component root directory.
* @throws IOException
* if any I/O exception occurred.
*/
public static String extractPEARFile(String pearFileLocation, File installationDir,
boolean cleanTarget) throws IOException {
return extractFilesFromPEARFile(pearFileLocation, null, installationDir, cleanTarget);
}
/**
* Internal implementation of the <code>extractPEARFile</code> method, which allows sending
* messages to the OUT and ERR queues.
*
* @param componentId
* The given component ID.
* @param pearFileLocation
* The given PEAR file location.
* @param installationDir
* The given target directory.
* @param controller
* The instance of the <code>InstallationController<code> class that provides OUT and ERR
* message routing, or <code>null</code>.
* @param cleanTarget
* If true, the target directory is cleaned before the PEAR file is installed to it.
* @return The path to the new component root directory.
* @throws IOException if any I/O exception occurred.
*/
protected static String extractPEARFile(String pearFileLocation, File installationDir,
InstallationController controller, boolean cleanTarget) throws IOException {
return extractFilesFromPEARFile(pearFileLocation, null, installationDir, controller,
cleanTarget);
}
/**
* Creates a <code>Hashtable</code> that contains (compId, InsD) pairs for all separate delegate
* components specified in a given installation table.
*
* @param installationTable
* The given installation table that specifies (compId, rootDirPath) pairs for all
* separate delegate components.
* @return The <code>Hashtable</code> that contains (compId, InsD) pairs for all separate
* delegate components specified in the given installation table.
* @throws IOException
* If an I/O exception occurred while loading the installation descriptor files.
*/
protected static Hashtable getDelegateInstallationDescriptors(Hashtable installationTable)
throws IOException {
// get list of separately installed delegate components
Enumeration dlgIdList = installationTable.keys();
// build Hashtable of delegate InsD objects
Hashtable dlgInsdObjects = new Hashtable();
while (dlgIdList.hasMoreElements()) {
// process next delegate component
String dlgId = (String) dlgIdList.nextElement();
String dlgRootPath = (String) installationTable.get(dlgId);
// get InsD object for this component
PackageBrowser dlgBrowser = new PackageBrowser(new File(dlgRootPath));
InstallationDescriptor dlgInsdObject = dlgBrowser.getInstallationDescriptor();
dlgInsdObjects.put(dlgId, dlgInsdObject);
}
return dlgInsdObjects;
}
/**
* @return The local host IP address.
*/
public static String getHostIpAddress() {
String hostAddress = "127.0.0.1";
try {
hostAddress = InetAddress.getLocalHost().getHostAddress();
} catch (Exception e) {
// in case of errors use localhost IP address
}
return hostAddress;
} // end of getHostIpAddress() method
/**
* Retrieves the root directory path of a given component, installed in the local file system, by
* using a given <code>PackageSelector</code> input. If the given <code>PackageSelector</code>
* is <code>null</code>, the default <code>PackageSelector</code> implementation is used.
*
* @param componentId
* The given installed component ID.
* @param pkgSelector
* The instance of the
* <code>PackageSelector<code> class that allows selecting root directory
* of the installed component in the local file system.
* @return The root directory path of the given component in the local
* file system, or <code>null</code>, if the component is not installed.
*/
protected static String getInstalledComponentRootPath(String componentId,
PackageSelector pkgSelector) {
String componentRootPath = null;
PackageSelector packageSelector = (pkgSelector != null) ? pkgSelector
: new SimplePackageSelector();
// check if this component is already installed locally
File componentRootDir = packageSelector.selectPackageDirectory(componentId);
if (componentRootDir != null)
componentRootPath = componentRootDir.getAbsolutePath();
return componentRootPath;
} // end of getInstalledComponentRootPath() method
/**
* Gets the PEAR file location (file path or URL) for a given component by using SITH DB a given
* <code>PackageSelector</code> input. If the given <code>PackageSelector</code> is
* <code>null</code>, the default <code>PackageSelector</code> implementation is used.
*
* @param componentId
* The given component ID.
* @param pkgSelector
* The instance of the
* <code>PackageSelector<code> class that allows selecting location of the
* given component PEAR file in the local file system, or in the network.
* @return The location of the PEAR file for the given component, or
* <code>null</code>, if the PEAR file was not found.
*/
protected static String getPEARFileLocation(String componentId, PackageSelector pkgSelector) {
String pearFileLocation = null;
PackageSelector packageSelector = (pkgSelector != null) ? pkgSelector
: new SimplePackageSelector();
// check if the PEAR file is in the local FS
File pearFile = packageSelector.selectPackageFile(componentId);
if (pearFile != null)
pearFileLocation = pearFile.getAbsolutePath();
else {
// enter PEAR file URL
URL pearFileUrl = packageSelector.selectPackageUrl(componentId);
if (pearFileUrl != null)
pearFileLocation = pearFileUrl.toString();
}
return pearFileLocation;
} // end of getPEARFileLocation()
/**
* Starts the application. This application expects the following JVM run-time settings:
* <ul>
* <li>-DUIMA_HOME=<local_uima_root_dir>
* </ul>
*
* @param args
* {-local pear_file | main_component_id} [-default] [installation_dir]
*/
public static void main(String[] args) {
// check args
if (args.length < 1) {
System.err.println("usage: InstallationController "
+ "{-local pear_file | main_component_id} [-root] " + "[installation_dir]");
return;
}
File localPearFile = null;
String mainComponentId = null;
boolean installInRootDir = false;
File installationDir = null;
// check local mode
int argNo = 0;
if (args[argNo].equals(LOCAL_OPT)) {
// work in 'local' mode
setLocalMode(true);
if (args.length > argNo + 1)
localPearFile = new File(args[++argNo]);
} else {
// work in standard SITH mode
mainComponentId = args[argNo++];
}
// parse command line
for (int i = argNo; i < args.length; i++) {
if (args[i].equals(INSTALL_IN_ROOT_OPT)) {
installInRootDir = true;
} else {
installationDir = new File(args[i]);
}
}
if (localPearFile == null && mainComponentId == null) {
System.err.println("usage: InstallationController "
+ "{-local pear_file | main_component_id} " + "[-root] [installation_dir]");
return;
}
// check input parameters
if (localPearFile != null && !localPearFile.exists()) {
System.err.println("[InstallationController]: " + localPearFile.getAbsolutePath()
+ " file not found");
}
if (installationDir == null) // set CWD by default
installationDir = new File(".");
else if (!installationDir.isDirectory()) {
System.err.println("[InstallationController]: " + installationDir.getAbsolutePath()
+ " directory not found");
return;
}
// in local mode - get component ID from PEAR file
if (__inLocalMode) {
try {
JarFile jarFile = new JarFile(localPearFile);
InstallationDescriptorHandler insdHandler = new InstallationDescriptorHandler();
insdHandler.parseInstallationDescriptor(jarFile);
InstallationDescriptor insd = insdHandler.getInstallationDescriptor();
if (insd != null)
mainComponentId = insd.getMainComponentId();
else
throw new FileNotFoundException("installation descriptor not found");
} catch (Exception err) {
System.err.println("[InstallationController]: terminated \n" + err.toString());
return;
}
}
// create instance of InstallationController
InstallationController controller = __inLocalMode ? new InstallationController(mainComponentId,
localPearFile, installationDir, installInRootDir) : new InstallationController(
mainComponentId, installationDir.getAbsolutePath(), installInRootDir);
// set PackageSelector
controller.setPackageSelector(new PackageSelectorGUI());
// 1st step: install main component
if (controller.installComponent() == null) {
// installation failed
controller.getErrMsgWriter().println(
"[InstallationController]: installation of " + mainComponentId + " failed => \n"
+ controller.getInstallationMsg());
} else {
try {
controller.getOutMsgWriter().println(
"[InstallationController]: installation of " + mainComponentId + " completed");
// 2nd step: verify main component installation
if (controller.verifyComponent()) {
controller.getOutMsgWriter().println(
"[InstallationController]: verification of " + mainComponentId + " completed");
controller.getOutMsgWriter().println(
"[InstallationController]: " + mainComponentId + " installed in the "
+ controller._mainComponentRootPath + " directory.");
} else {
controller.getOutMsgWriter().println(
"[InstallationController]: verification of " + mainComponentId + " failed => \n"
+ controller.getVerificationMsg());
}
} catch (Exception exc) {
System.err.println("Error in InstallationController.main(): " + exc.toString());
exc.printStackTrace(System.err);
}
}
// stop controller messaging service
controller.terminate();
System.exit(0);
}
/**
* Switches between the 'local' and 'DB' modes, depending on a given <code>boolean</code> flag.
*
* @param inLocalMode
* if <code>true</code> the utility operates in the 'local' mode, otherwise it operates
* in the 'DB' mode.
*/
public static synchronized void setLocalMode(boolean inLocalMode) {
__inLocalMode = inLocalMode;
}
/**
* Runs the installation test for a given installed pear component, and returns the
* <code>TestStatus</code> object with the test results.
*
* @param pkgBrowser
* The given package borwser object of the installed pear package.
* @return The <code>TestStatus</code> object that contains the return code and possible error
* message of the test.
*/
public static synchronized TestStatus verifyComponentInstallation(PackageBrowser pkgBrowser) {
try {
// check package browser parameters
if (pkgBrowser != null) {
if (pkgBrowser.getInstallationDescriptor() == null) {
throw new PackageInstallerException(PEAR_MESSAGE_RESOURCE_BUNDLE,
"installation_verification_install_desc_not_available");
}
if (pkgBrowser.getInstallationDescriptor().getMainComponentDesc() == null) {
throw new PackageInstallerException(PEAR_MESSAGE_RESOURCE_BUNDLE,
"installation_verification_main_desc_not_available", new Object[] { pkgBrowser
.getInstallationDescriptor().getMainComponentId() });
}
if (pkgBrowser.getInstallationDescriptor().getMainComponentRoot() == null) {
throw new PackageInstallerException(PEAR_MESSAGE_RESOURCE_BUNDLE,
"installation_verification_main_root_not_available", new Object[] { pkgBrowser
.getInstallationDescriptor().getMainComponentId() });
}
}
// create InstallationTester object
InstallationTester installTester = new InstallationTester(pkgBrowser);
return installTester.doTest();
} catch (Throwable exc) {
// print exception as 'verification message'
StringWriter strWriter = new StringWriter();
PrintWriter oWriter = new PrintWriter(strWriter);
exc.printStackTrace(oWriter);
TestStatus status = new TestStatus();
status.setRetCode(TestStatus.TEST_NOT_SUCCESSFUL);
status.setMessage(strWriter.toString());
return status;
}
}
/**
* Constructs an instance of the <code>InstallationController</code> class for a given component
* and a given installation root directory. By default, the <code>InstallationController</code>
* creates a <code>component_id</code> subdirectory for the component code and resources. By
* default, the <code>InstallationController</code> class sends all stdout and stderr messages
* to the default message listener, which prints them to the standard console streams.
*
* @param componentId
* The given component ID.
* @param rootDirPath
* The given installation root directory path.
*/
public InstallationController(String componentId, String rootDirPath) {
this(componentId, rootDirPath, false);
}
/**
* Constructs an instance of the <code>InstallationController</code> class for a given component
* and a given installation root directory. If the <code>installInRootDir</code> flag is
* <code>true</code>, the component will be installed in the given root directory, otherwise
* the <code>InstallationController</code> will create a <code>component_id</code>
* subdirectory for the component code and resources. By default, the
* <code>InstallationController</code> class sends all stdout and stderr messages to the default
* message listener, which prints them to the standard console streams.
*
* @param componentId
* The given component ID.
* @param rootDirPath
* The given installation root directory path.
* @param installInRootDir
* If <code>true</code>, the component will be installed in the given root directory,
* otherwise it will be installed in the <code>component_id</code> subdirectory of the
* root directory. Note: the installation directory will be cleaned before the PEAR file is
* installed to it.
*/
public InstallationController(String componentId, String rootDirPath, boolean installInRootDir) {
this(componentId, rootDirPath, installInRootDir, null);
}
/**
* Constructs an instance of the <code>InstallationController</code> class for a given component
* and a given installation root directory. If the <code>installInRootDir</code> flag is
* <code>true</code>, the component will be installed in the given root directory, otherwise
* the <code>InstallationController</code> will create a <code>component_id</code>
* subdirectory for the component code and resources. If a given custom message listener is not
* <code>null</code>, the <code>InstallationController</code> instance will sends all stdout
* and stderr messages to the given message listener, otherwise these messages are sent to the
* default message listener, which prints them to the standard console streams.
*
* @param componentId
* The given component ID.
* @param rootDirPath
* The given installation root directory path.
* @param installInRootDir
* If <code>true</code>, the component will be installed in the given root directory,
* otherwise it will be installed in the <code>component_id</code> subdirectory of the
* root directory. Note: the installation directory will be cleaned before the PEAR file is
* installed to it.
* @param msgListener
* The given custom message listener or <code>null</code>.
*/
public InstallationController(String componentId, String rootDirPath, boolean installInRootDir,
MessageRouter.StdChannelListener msgListener) {
this(componentId, rootDirPath, installInRootDir, null, msgListener, true);
// print program information
getOutMsgWriter().println(
"[InstallationController]: " + "OS - " + __osName + ", Host - " + _hostIpAddress);
if (__inLocalMode) {
getOutMsgWriter().println("[InstallationController]: " + "working in 'local' mode");
}
}
/**
* Internal constructor that creates an instance of the <code>InstallationController</code>
* class for a given component and a given installation root directory. If the
* <code>installInRootDir</code> flag is <code>true</code>, the component will be installed
* in the given root directory, otherwise the <code>InstallationController</code> will create a
* <code>component_id</code> subdirectory for the component code and resources. If a given
* custom <code>MessageRouter</code> is not <code>null</code>, the new
* <code>InstallationController</code> instance will use the given message router, otherwise it
* will create a new message router object. If a given custom message listener is not
* <code>null</code>, the <code>InstallationController</code> instance will send all stdout
* and stderr messages to the given message listener, otherwise these messages are sent to the
* default message listener, which prints them to the standard console streams.
*
* @param componentId
* The given component ID.
* @param rootDirPath
* The given installation root directory.
* @param installInRootDir
* If <code>true</code>, the component will be installed in the given root directory,
* otherwise it will be installed in the <code>component_id</code> subdirectory of the
* root directory.
* @param msgRouter
* The given custom <code>MessageRouter</code> object or <code>null</code>.
* @param msgListener
* The given custom message listener object or <code>null</code>.
* @param cleanInstallDir
* If <code>true</code>, the target installation directory will be cleaned before the
* PEAR file is installed.
*/
protected InstallationController(String componentId, String rootDirPath,
boolean installInRootDir, MessageRouter msgRouter,
MessageRouter.StdChannelListener msgListener, boolean cleanInstallDir) {
if (msgRouter == null)
_msgRouter = new MessageRouter();
else
_msgRouter = msgRouter;
if (msgListener == null) {
// set default standard message channel listener
_defaultMsgListener = new MessageRouter.StdChannelListener() {
public void errMsgPosted(String errMsg) {
System.err.print(errMsg);
System.err.flush();
}
public void outMsgPosted(String outMsg) {
System.out.print(outMsg);
System.out.flush();
}
};
} else
// set custom standard message channel listener
_defaultMsgListener = msgListener;
addMsgListener(_defaultMsgListener);
if (!_msgRouter.isRunning()) // start messenger
_msgRouter.start();
// initialize attributes
_mainComponentId = componentId;
_cleanInstallDir = cleanInstallDir;
if (installInRootDir) {
_mainComponentRootPath = rootDirPath;
_mainComponentRoot = new File(_mainComponentRootPath);
_installationDir = _mainComponentRoot.getParentFile();
_installationDirPath = _installationDir.getAbsolutePath();
} else {
_installationDirPath = rootDirPath;
_installationDir = new File(_installationDirPath);
_mainComponentRoot = new File(_installationDir, componentId);
_mainComponentRootPath = _mainComponentRoot.getAbsolutePath();
}
_uimaHomePath = System.getProperty(UIMA_HOME_ENV);
if (_uimaHomePath != null)
_uimaHomePath = _uimaHomePath.replace('\\', '/');
if (__osName == null)
__osName = System.getProperty("os.name");
_hostIpAddress = getHostIpAddress();
// set default package selector
_packageSelector = new SimplePackageSelector(getOutMsgWriter(), getErrMsgWriter());
}
/**
* Constructor for the 'local' mode, which specifies component ID, local PEAR file and a local
* root directory where the component will be installed. By default, the
* <code>InstallationController</code> creates a <code>component_id</code> subdirectory for
* the component code and resources. By default, the <code>InstallationController</code> class
* sends all stdout and stderr messages to the default message listener, which prints them to the
* standard console streams.
*
* @param componentId
* The given component ID.
* @param localPearFile
* The given local PEAR file.
* @param rootDir
* The given local root directory for installation.
*/
public InstallationController(String componentId, File localPearFile, File rootDir) {
this(componentId, rootDir.getAbsolutePath(), false, null, null, true);
_mainPearFileLocation = localPearFile.getAbsolutePath();
}
/**
* Constructor for the 'local' mode, which specifies component ID, local PEAR file and a local
* root directory where the component will be installed. If the <code>installInRootDir</code>
* flag is <code>true</code>, the component code and resources will be installed in the
* specified root directory, otherwise the <code>InstallationController</code> will create a
* <code>component_id</code> subdirectory for the component code and resources. By default, the
* <code>InstallationController</code> class sends all stdout and stderr messages to the default
* message listener, which prints them to the standard console streams.
*
* @param componentId
* The given component ID.
* @param localPearFile
* The given local PEAR file.
* @param rootDir
* The given local root directory for installation.
* @param installInRootDir
* If <code>true</code>, the component will be installed in the given root directory,
* otherwise it will be installed in the <code>component_id</code> subdirectory of the
* root directory.
* @param cleanInstallDir
* If <code>true</code>, the target installation directory will be cleaned before the
* PEAR file is installed.
*/
public InstallationController(String componentId, File localPearFile, File rootDir,
boolean installInRootDir, boolean cleanInstallDir) {
this(componentId, rootDir.getAbsolutePath(), installInRootDir, null, null, cleanInstallDir);
_mainPearFileLocation = localPearFile.getAbsolutePath();
}
/**
* Constructor for the 'local' mode, which specifies component ID, local PEAR file and a local
* root directory where the component will be installed. If the <code>installInRootDir</code>
* flag is <code>true</code>, the component code and resources will be installed in the
* specified root directory, otherwise the <code>InstallationController</code> will create a
* <code>component_id</code> subdirectory for the component code and resources. By default, the
* <code>InstallationController</code> class sends all stdout and stderr messages to the default
* message listener, which prints them to the standard console streams.
*
* @param componentId
* The given component ID.
* @param localPearFile
* The given local PEAR file.
* @param rootDir
* The given local root directory for installation.
* @param installInRootDir
* If <code>true</code>, the component will be installed in the given root directory,
* otherwise it will be installed in the <code>component_id</code> subdirectory of the
* root directory. Note: the installation directory will be cleaned before the PEAR file is
* installed to it.
*/
public InstallationController(String componentId, File localPearFile, File rootDir,
boolean installInRootDir) {
this(componentId, rootDir.getAbsolutePath(), installInRootDir, null, null, true);
_mainPearFileLocation = localPearFile.getAbsolutePath();
}
/**
* Constructor for the 'local' mode, which specifies component ID, local PEAR file and a local
* root directory where the component will be installed. If the <code>installInRootDir</code>
* flag is <code>true</code>, the component code and resources will be installed in the
* specified root directory, otherwise the <code>InstallationController</code> will create a
* <code>component_id</code> subdirectory for the component code and resources. If the custom
* message listener is not <code>null</code>, the <code>InstallationController</code> class
* sends all stdout and stderr messages to this message listener, otherwise these messages are
* sent to the default message listener, which prints them to the standard console streams.
*
* @param componentId
* The given component ID.
* @param localPearFile
* The given local PEAR file.
* @param rootDir
* The given local root directory for installation.
* @param installInRootDir
* If <code>true</code>, the component will be installed in the given root directory,
* otherwise it will be installed in the <code>component_id</code> subdirectory of the
* root directory.
* @param msgListener
* The given custom message listener or <code>null</code>.
*/
public InstallationController(String componentId, File localPearFile, File rootDir,
boolean installInRootDir, MessageRouter.StdChannelListener msgListener) {
this(componentId, rootDir.getAbsolutePath(), installInRootDir, null, msgListener, true);
_mainPearFileLocation = localPearFile.getAbsolutePath();
}
/**
* Constructor for the 'local' mode, which specifies component ID, local PEAR file and a local
* root directory where the component will be installed. If the <code>installInRootDir</code>
* flag is <code>true</code>, the component code and resources will be installed in the
* specified root directory, otherwise the <code>InstallationController</code> will create a
* <code>component_id</code> subdirectory for the component code and resources. If the custom
* message listener is not <code>null</code>, the <code>InstallationController</code> class
* sends all stdout and stderr messages to this message listener, otherwise these messages are
* sent to the default message listener, which prints them to the standard console streams.
*
* @param componentId
* The given component ID.
* @param localPearFile
* The given local PEAR file.
* @param rootDir
* The given local root directory for installation.
* @param installInRootDir
* If <code>true</code>, the component will be installed in the given root directory,
* otherwise it will be installed in the <code>component_id</code> subdirectory of the
* root directory.
* @param msgListener
* The given custom message listener or <code>null</code>.
* @param cleanInstallDir
* If <code>true</code>, the target installation directory will be cleaned before the
* PEAR file is installed.
*/
public InstallationController(String componentId, File localPearFile, File rootDir,
boolean installInRootDir, MessageRouter.StdChannelListener msgListener,
boolean cleanInstallDir) {
this(componentId, rootDir.getAbsolutePath(), installInRootDir, null, msgListener,
cleanInstallDir);
_mainPearFileLocation = localPearFile.getAbsolutePath();
}
/**
* Adds a given object, implementing the <code>MessageRouter.StdChannelListener</code> interface
* to the list of standard channel listeners.
*
* @param listener
* The given <code>MessageRouter.StdChannelListener</code> object to be added to the
* list.
*/
public void addMsgListener(MessageRouter.StdChannelListener listener) {
_msgRouter.addChannelListener(listener);
}
/**
* Builds <code>CLASSPATH</code> for the installed component, including <code>CLASSPATH</code>
* for all separate delegate components that are utilized by the main installed component, if any.
*
* @return The <code>CLASSPATH</code> for the installed component, or <code>null</code>, if
* the component has not been installed.
* @throws IOException
* If any I/O exception occurred.
*/
public String buildComponentClassPath() throws IOException {
if (_insdObject != null) {
StringBuffer cpBuffer = new StringBuffer();
// build main component classpath
String mainClassPath = buildComponentClassPath(_mainComponentRootPath, _insdObject);
cpBuffer.append(mainClassPath);
// add component classpath for possible delegate components
if (_installationTable.size() > 0) {
Enumeration dlgIdList = _installationTable.keys();
while (dlgIdList.hasMoreElements()) {
// process next delegate component
String dlgId = (String) dlgIdList.nextElement();
String dlgRootPath = (String) _installationTable.get(dlgId);
InstallationDescriptor dlgInsD = (InstallationDescriptor) _installationInsDs.get(dlgId);
String dlgClassPath = buildComponentClassPath(dlgRootPath, dlgInsD);
if (dlgClassPath.length() > 0) {
if (cpBuffer.length() > 0
&& cpBuffer.charAt(cpBuffer.length() - 1) != File.pathSeparatorChar)
cpBuffer.append(File.pathSeparatorChar);
cpBuffer.append(dlgClassPath);
}
}
}
return cpBuffer.toString();
}
return null;
}
/**
* Builds <code>PATH</code> for the installed component, including <code>PATH</code> for all
* separate delegate components that are utilized by the main installed component, if any.
*
* @return The <code>PATH</code> for the installed component, or <code>null</code>, if the
* component has not been installed.
*/
public String buildComponentPath() {
if (_insdObject != null) {
StringBuffer pBuffer = new StringBuffer();
// build main component path
String mainPath = buildComponentPath(_mainComponentRootPath, _insdObject);
pBuffer.append(mainPath);
// add component path for possible delegate components
if (_installationTable.size() > 0) {
Enumeration dlgIdList = _installationTable.keys();
while (dlgIdList.hasMoreElements()) {
// process next delegate component
String dlgId = (String) dlgIdList.nextElement();
String dlgRootPath = (String) _installationTable.get(dlgId);
InstallationDescriptor dlgInsD = (InstallationDescriptor) _installationInsDs.get(dlgId);
String dlgPath = buildComponentPath(dlgRootPath, dlgInsD);
if (dlgPath.length() > 0) {
if (pBuffer.length() > 0
&& pBuffer.charAt(pBuffer.length() - 1) != File.pathSeparatorChar)
pBuffer.append(File.pathSeparatorChar);
pBuffer.append(dlgPath);
}
}
}
return pBuffer.toString();
}
return null;
}
/**
* Builds <code>Properties</code> table of required environment variables for the installed
* component, including environment variables for all separate delegate components that are
* utilized by the main installed component, if any.
*
* @return <code>Properties</code> table of required environment variables for the installed
* component, or <code>null</code>, if the component has not been installed.
*/
public Properties buildTableOfEnvVars() {
if (_insdObject != null) {
// set required env vars for main component
Properties envVars = buildTableOfEnvVars(_insdObject);
// add required env vars for possible delegate components
if (_installationTable.size() > 0) {
Enumeration dlgIdList = _installationTable.keys();
while (dlgIdList.hasMoreElements()) {
// process next delegate component
String dlgId = (String) dlgIdList.nextElement();
InstallationDescriptor dlgInsD = (InstallationDescriptor) _installationInsDs.get(dlgId);
Properties dlgEnvVars = buildTableOfEnvVars(dlgInsD);
envVars = StringUtil.appendProperties(envVars, dlgEnvVars, false);
}
}
return envVars;
}
return null;
}
/**
* Overrides standard <code>finalize</code> method.
*/
protected void finalize() {
_msgRouter.terminate();
}
/**
* Performs installation of the specified component in the specified target directory, including
* all delegate components (if exist). If the installation completed successfully, returns the
* <code>InstallationDescriptor</code> object for the installed component. If the installation
* failed, returns <code>null</code>, and sets the installation error message that can be
* retrieved using the <code>getInstallationMsg()</code> method.
*
* @return The <code>InstallationDescriptor</code> object for the installed component, if the
* installation succeeded, <code>null</code> otherwise.
*/
public synchronized InstallationDescriptor installComponent() {
try {
if (_installationMonitor != null) // notify installation monitor
_installationMonitor.setInstallationStatus(_mainComponentId, INSTALLATION_IN_PROGRESS);
if (_mainPearFileLocation == null) // get PEAR file location
_mainPearFileLocation = getPEARFileLocation(_mainComponentId, _packageSelector);
// extract PEAR file in a specified directory
if (extractPEARFile(_mainPearFileLocation, _mainComponentRoot, this, _cleanInstallDir) == null) {
// PEAR extraction failed
// set error message
setInstallationError(new IOException("PEAR extraction failed"));
if (_installationMonitor != null) // notify monitor
_installationMonitor.setInstallationStatus(_mainComponentId, INSTALLATION_FAILED);
return null;
}
// load installation descriptor
InstallationDescriptorHandler insdHandler = new InstallationDescriptorHandler();
File insdFile = new File(_mainComponentRoot, InstallationProcessor.INSD_FILE_PATH);
insdHandler.parse(insdFile);
_insdObject = insdHandler.getInstallationDescriptor();
// install separate delegate components
installDelegateComponents();
// complete installation process for main component
InstallationProcessor processor = new InstallationProcessor(_mainComponentRootPath,
_installationTable, this);
processor.process();
_insdObject = processor.getInstallationDescriptor();
// save modified installation descriptor file
saveInstallationDescriptorFile();
// generate PEAR.properties file
generatePackageConfigFile();
// generate 'setenv.bat' file
generateSetEnvFile();
generatePearSpecifier(_mainComponentRootPath, _mainComponentId);
getOutMsgWriter().println(
"[InstallationController]: " + "the " + SET_ENV_FILE + " file contains required "
+ "environment variables for this component");
getOutMsgWriter().println(
"[InstallationController]: component " + _mainComponentId
+ " installation completed.");
if (_installationMonitor != null) {
// notify installation monitor
_installationMonitor.setInstallationLocation(_mainComponentId, _mainComponentRootPath);
_installationMonitor.setInstallationStatus(_mainComponentId, INSTALLATION_COMPLETED);
}
} catch (Exception exc) {
getErrMsgWriter().println("Error in InstallationController: " + exc);
exc.printStackTrace(getErrMsgWriter());
// set error message
setInstallationError(exc);
if (_installationMonitor != null) // notify monitor
_installationMonitor.setInstallationStatus(_mainComponentId, INSTALLATION_FAILED);
return null;
}
return _insdObject;
}
/**
* Performs installation of XML descriptors of the specified component in the specified target
* directory, including XML descriptors of all the delegate components (if exist). If the
* installation completed successfully, returns the <code>InstallationDescriptor</code> object
* for the partially installed component. If the installation failed, returns <code>null</code>,
* and sets the installation error message that can be retrieved using the
* <code>getInstallationMsg()</code> method.
*
* @return The <code>InstallationDescriptor</code> object for the partially installed component,
* if the installation succeeded, <code>null</code> otherwise.
*/
public synchronized InstallationDescriptor installComponentDescriptors() {
try {
if (_mainPearFileLocation == null) // get PEAR file location
_mainPearFileLocation = getPEARFileLocation(_mainComponentId, _packageSelector);
// extract main XML descriptors in a specified directory
if (extractFilesFromPEARFile(_mainPearFileLocation, ".xml", _mainComponentRoot, this,
_cleanInstallDir) == null) {
// PEAR extraction failed
// set error message
setInstallationError(new IOException("PEAR extraction failed"));
return null;
}
// load installation descriptor
InstallationDescriptorHandler insdHandler = new InstallationDescriptorHandler();
File insdFile = new File(_mainComponentRoot, InstallationProcessor.INSD_FILE_PATH);
insdHandler.parse(insdFile);
_insdObject = insdHandler.getInstallationDescriptor();
// install XML descriptors of separate delegate components
installDelegateComponentsDescriptors();
// complete installation process for main component
InstallationProcessor processor = new InstallationProcessor(_mainComponentRootPath,
_installationTable);
processor.process();
_insdObject = processor.getInstallationDescriptor();
getOutMsgWriter().println(
"[InstallationController]: component " + _mainComponentId
+ " descriptors installation completed.");
} catch (Exception exc) {
getErrMsgWriter().println("Error in InstallationController: " + exc);
exc.printStackTrace(getErrMsgWriter());
// set error message
setInstallationError(exc);
return null;
}
return _insdObject;
}
/**
* Performs installation of all separate delegate components for the specified main component.
*
*/
protected synchronized void installDelegateComponents() {
// get list of separate delegate components IDs
Enumeration dlgList = _insdObject.getDelegateComponents().keys();
while (dlgList.hasMoreElements()) {
// get next separate delegate component ID
String componentId = (String) dlgList.nextElement();
// check if the same component is available (not in use)
String componentRootPath = null;
try {
componentRootPath = getInstalledComponentRootPath(componentId, _packageSelector);
} catch (Exception e) {
getErrMsgWriter().println(
"[InstallationController]: " + "failed to query " + componentId + " location - "
+ e);
}
if (componentRootPath == null) {
// install next separate delegate component
InstallationController dlgController = new InstallationController(componentId,
_installationDirPath, false, this._msgRouter, this._defaultMsgListener,
_cleanInstallDir);
dlgController.setPackageSelector(this._packageSelector);
InstallationDescriptor dlgInsdObject = dlgController.installComponent();
if (dlgInsdObject == null) {
getErrMsgWriter().println(
"[InstallationController]: " + "failed to install dlg component " + componentId);
throw new RuntimeException("failed to install dlg component " + componentId);
}
componentRootPath = dlgInsdObject.getMainComponentRoot();
// add installation info to the table
_installationTable.put(componentId, componentRootPath);
_installationInsDs.put(componentId, dlgInsdObject);
} else {
// add installation info to the table
_installationTable.put(componentId, componentRootPath);
// get InsD object for this component
try {
PackageBrowser dlgBrowser = new PackageBrowser(new File(componentRootPath));
InstallationDescriptor dlgInsdObject = dlgBrowser.getInstallationDescriptor();
_installationInsDs.put(componentId, dlgInsdObject);
} catch (IOException e) {
// this should never happen
}
}
}
}
/**
* Performs installation of XML descriptors for all separate delegate components of the specified
* main component.
*/
protected synchronized void installDelegateComponentsDescriptors() {
// get list of separate delegate components IDs
Enumeration dlgList = _insdObject.getDelegateComponents().keys();
while (dlgList.hasMoreElements()) {
// get next separate delegate component ID
String componentId = (String) dlgList.nextElement();
// install XML descriptors of the next delegate component
InstallationController dlgController = new InstallationController(componentId,
_installationDirPath, false, this._msgRouter, this._defaultMsgListener,
_cleanInstallDir);
dlgController.setPackageSelector(this._packageSelector);
InstallationDescriptor dlgInsdObject = dlgController.installComponentDescriptors();
if (dlgInsdObject == null) {
getErrMsgWriter().println(
"[InstallationController]: " + "failed to install descriptors for dlg component "
+ componentId);
throw new RuntimeException("failed to install descriptors for dlg component " + componentId);
}
String componentRootPath = dlgInsdObject.getMainComponentRoot();
// add installation info to the table
_installationTable.put(componentId, componentRootPath);
_installationInsDs.put(componentId, dlgInsdObject);
}
}
/**
* generates the pearSpecifier to run the installed pear component. The descriptor that is created
* has the filename &lt;componentID&gt;_pear.xml and is created in the main component root
* directory. If the file already exist, it will be overridden.
*
* @param mainComponentRootPath
* main component root path where the pear was installed to
*
* @param mainComponentId
* main component ID of the installed pear file
*
* @throws IOException
* @throws SAXException
*/
protected static synchronized void generatePearSpecifier(String mainComponentRootPath,
String mainComponentId) throws IOException, SAXException {
PearSpecifier pearSpec = UIMAFramework.getResourceSpecifierFactory().createPearSpecifier();
pearSpec.setPearPath(mainComponentRootPath);
File outputFile = new File(mainComponentRootPath, mainComponentId + PEAR_DESC_FILE_POSTFIX);
pearSpec.toXML(new FileOutputStream(outputFile));
}
/**
* Generates the file (batch file for Windows) containing specific environment variables that
* should be used to run the component.
*
* @throws IOException
* if any I/O exception occurred.
*/
protected synchronized void generateSetEnvFile() throws IOException {
File setEnvFile = new File(_mainComponentRoot, SET_ENV_FILE);
PrintWriter fWriter = null;
try {
fWriter = new PrintWriter(new FileWriter(setEnvFile));
fWriter.println("### Add the following environment variables");
fWriter.println("### to appropriate existing environment variables");
fWriter.println("### to run the " + _mainComponentId + " component");
fWriter.println();
// CLASSPATH
String classPath = buildComponentClassPath();
if (classPath.length() > 0)
fWriter.println("CLASSPATH=" + classPath);
// PATH
String path = buildComponentPath();
if (path.length() > 0)
fWriter.println("PATH=" + path);
// the rest of env.vars.
Properties envVarTable = buildTableOfEnvVars();
Enumeration envVarList = envVarTable.keys();
while (envVarList.hasMoreElements()) {
String varName = (String) envVarList.nextElement();
String varValue = envVarTable.getProperty(varName);
// add env.var. setting
if (varName.length() > 0 && varValue.length() > 0
&& !varName.equalsIgnoreCase(CLASSPATH_VAR) && !varName.equalsIgnoreCase(PATH_VAR)) {
fWriter.println(varName + "=" + varValue);
}
}
} finally {
if (fWriter != null) {
try {
fWriter.close();
} catch (Exception e) {
// ignore close exception
}
}
}
}
/**
* Generates/updates the PEAR configuration file setting the main component root directory, as
* well as root directories of all related delegate components.
*
* @throws IOException
* if any I/O exception occurred.
*/
protected synchronized void generatePackageConfigFile() throws IOException {
Properties packageConfig = new Properties();
File packageConfigFile = new File(_mainComponentRoot, PACKAGE_CONFIG_FILE);
if (packageConfigFile.exists()) {
// loading existing pear config file
InputStream iStream = null;
try {
iStream = new FileInputStream(packageConfigFile);
packageConfig.load(iStream);
} finally {
if (iStream != null) {
try {
iStream.close();
} catch (Exception e) {
// ignore close exception
}
}
}
}
// set local config params
packageConfig.setProperty(LocalInstallationAgent.MAIN_ROOT, _mainComponentRootPath.replace(
'\\', '/'));
Iterator dlgIdList = _installationTable.keySet().iterator();
while (dlgIdList.hasNext()) {
String id = (String) dlgIdList.next();
String idRoot = LocalInstallationAgent.COMP_ROOT_PREFIX + id
+ LocalInstallationAgent.COMP_ROOT_SUFFIX;
packageConfig.setProperty(idRoot, ((String) _installationTable.get(id)).replace('\\', '/'));
}
// save pear config file
OutputStream oStream = null;
try {
String header = _mainComponentId;
oStream = new FileOutputStream(packageConfigFile);
packageConfig.store(oStream, header);
} finally {
if (oStream != null) {
try {
oStream.close();
} catch (Exception e) {
// ignore close exception
}
}
}
}
/**
* @return Error message writer for intraprocess messaging.
*/
protected PrintWriter getErrMsgWriter() {
return _msgRouter.errWriter();
}
/**
* @return The installation message (error message).
*/
public String getInstallationMsg() {
return _installationMsg;
}
/**
* @return Output message writer for intraprocess messaging.
*/
protected PrintWriter getOutMsgWriter() {
return _msgRouter.outWriter();
}
/**
* @return The verification message (error message).
*/
public String getVerificationMsg() {
return _verificationMsg;
}
/**
* Removes a given <code>MessageRouter.StdChannelListener</code> object from the list of
* standard channel listeners.
*
* @param listener
* The given <code>MessageRouter.StdChannelListener</code> object to be removed from
* the list.
*/
public void removeMsgListener(MessageRouter.StdChannelListener listener) {
_msgRouter.removeChannelListener(listener);
}
/**
* Saves modified installation descriptor file.
*
* @throws IOException
* if any I/O exception occurred.
*/
public synchronized void saveInstallationDescriptorFile() throws IOException {
if (_insdObject != null) {
File insdFile = _insdObject.getInstallationDescriptorFile();
InstallationDescriptorHandler.saveInstallationDescriptor(_insdObject, insdFile);
}
}
/**
* Prints the stack trace of a given <code>Exception</code> object as the installation error
* message.
*
* @param error
* The given <code>Exception</code> object.
*/
protected synchronized void setInstallationError(Exception error) {
_installationMsg = StringUtil.errorStackTraceContent(error);
}
/**
* Plugs-in a given implementation of the <code>InstallationMonitor</code> interface.
*
* @param monitor
* The given implementation of the <code>InstallationMonitor</code> interface.
*/
public synchronized void setInstallationMonitor(InstallationMonitor monitor) {
if (monitor != null)
_installationMonitor = monitor;
}
/**
* Plugs-in a given implementation of the <code>PackageSelector</code> interface.
*
* @param selector
* The given implementation of the <code>PackageSelector</code> interface.
*/
public synchronized void setPackageSelector(PackageSelector selector) {
if (selector != null)
_packageSelector = selector;
}
/**
* Prints the stack trace of a given <code>Exception</code> object as the verification error
* message.
*
* @param error
* The given <code>Exception</code> object.
*/
protected synchronized void setVerificationError(Exception error) {
_verificationMsg = StringUtil.errorStackTraceContent(error);
}
/**
* Sets a given UIMA local home directory path.
*
* @param uimaHomePath
* The given UIMA local home directory path.
*/
public synchronized void setUimaHomePath(String uimaHomePath) {
_uimaHomePath = uimaHomePath.replace('\\', '/');
}
/**
* Terminates the <code>MessageRouter</code> thread. This method should be called after all the
* processing is finished.
*/
public void terminate() {
_msgRouter.terminate();
}
/**
* Verifies installations of the main component, and sets appropriate component status in the SITH
* DB.
*
* @return <code>true</code> if the verification completed successfully, <code>false</code>
* otherwise.
*/
public synchronized boolean verifyComponent() {
boolean success = false;
try {
if (_installationMonitor != null) // notify monitor
_installationMonitor.setInstallationStatus(_mainComponentId, VERIFICATION_IN_PROGRESS);
// create PackageBrowser object for the installed PEAR
PackageBrowser installedPear = new PackageBrowser(this._mainComponentRoot);
TestStatus status = verifyComponentInstallation(installedPear);
if (status.getRetCode() == TestStatus.TEST_SUCCESSFUL) {
// verification successful
success = true;
_verificationMsg = null;
if (_installationMonitor != null) // notify monitor
_installationMonitor.setInstallationStatus(_mainComponentId, VERIFICATION_COMPLETED);
} else if (status.getRetCode() == TestStatus.TEST_NOT_SUCCESSFUL) {
// verification failed
_verificationMsg = status.getMessage();
if (_installationMonitor != null) // notify monitor
_installationMonitor.setInstallationStatus(_mainComponentId, VERIFICATION_FAILED);
} else {
// verification cancelled
_verificationMsg = status.getMessage();
if (_installationMonitor != null) // notify monitor
_installationMonitor.setInstallationStatus(_mainComponentId, VERIFICATION_CANCELLED);
}
} catch (Exception err) {
_verificationMsg = err.toString();
if (_installationMonitor != null) // notify monitor
_installationMonitor.setInstallationStatus(_mainComponentId, VERIFICATION_FAILED);
}
return success;
}
}