blob: cce4504b57ad7de5a229e9f6e7cac3e7edb3aec8 [file] [log] [blame]
/* ZipSelfExtractor.java */
import java.io.*;
import java.net.*;
import java.util.zip.*;
import java.util.*;
import java.util.jar.*;
/**
* The abstract base for GemFire like products. This class
* needs to be extend to provide product specific
* names/functionality.
* @see {@link createJars}
* @see {@link getInstallDirProperty}
* @see {@link getInstallOpenSource}
* @see {@link getProductJarName}
*
* The following support files may also need to be customized.
* @see i18n.properties
* @see EULA.txt
* @author kbanks
*/
public abstract class ZipSelfExtractor
{
private boolean isInteractive;
/** This must match the name used in build.xml#build-installer */
private final static String OSS_JAR = "oss.jar";
private final static String OSS_PATTERN = ".*/opensource/.*";
private static int FILES_PER_DOT = 5;
private static final int YES = 1;
private static final int YES_TO_ALL = 2;
private static final int NO = 3;
private static final int CANCEL = 4;
protected File installDir;
private PropertyResourceBundle rb;
private Throwable error;
private static final String MANIFEST = "META-INF/MANIFEST.MF";
private static final String EULA = "EULA.txt";
private static final String I18N = "i18n";
/** A series of calls to {@link createJar} */
protected abstract void createJars() throws IOException;
/** @return the name of the property that specifies the
* installation directory for an unattended installation */
protected abstract String getInstallDirProperty();
/** @return the name of the boolean property that controls
* opensource installation */
protected abstract String getInstallOpenSourceProperty();
/** @return the name of the jar that contains the product */
protected abstract String getProductJarName();
private void setError(Throwable t) {
if(error == null) {
error = t;
}
}
private void handleFailure() throws Throwable {
if (error != null) {
if ( error instanceof InstallationCancelled ) {
System.out.println("");
Throwable cause = error.getCause();
if (cause != null) {
//intentionally not getLocalizedMessage()
System.out.println(cause.getMessage());
}
//intentionally not getLocalizedMessage()
System.out.println(error.getMessage());
System.exit(1);
}
System.out.println("");
System.out.println(rb.getString("INSTALLATION_FAILED"));
System.out.println(error.getLocalizedMessage());
throw error;
}
}
protected ZipSelfExtractor() {
String directory = System.getProperty( getInstallDirProperty(), ".");
if (System.getProperty(getInstallDirProperty(), null) == null) {
isInteractive = true;
} else {
// User specified an install directory as a system Property
// Assuming unattended installation.
isInteractive = false;
}
installDir = new File(directory);
rb = (PropertyResourceBundle) ResourceBundle.getBundle(I18N, Locale.getDefault());
}
private String getJarFileName() {
String myClassName = this.getClass().getName() + ".class";
URL urlJar = ClassLoader.getSystemResource(myClassName);
String urlStr = null;
try {
urlStr = URLDecoder.decode(urlJar.toString(), "UTF-8");
} catch ( UnsupportedEncodingException uee ) {
//This should never happen because UTF-8 is required to be implemented
throw new RuntimeException(uee);
}
int from = "jar:file:".length();
int to = urlStr.indexOf("!/");
String jarName = urlStr.substring(from, to);
return jarName;
}
private boolean getInstallOpenSource() {
return Boolean.getBoolean(getInstallOpenSourceProperty());
}
private void extract(String zipfile) {
System.out.println(rb.getString("BEGIN"));
File currentArchive = new File(zipfile);
displayEULA();
queryInstallDir();
byte[] buf = new byte[1024];
boolean overwrite = false;
ZipFile zf = null;
FileOutputStream out = null;
InputStream in = null;
try {
zf = new ZipFile(currentArchive);
int size = zf.size();
int extracted = 0;
System.out.print("");
System.out.print(rb.getString("UNZIPPING_FILES"));
Enumeration entries = zf.entries();
String innerClassName =
InstallationCancelled.class.getName() + ".class";
for (int i=0; i<size; i++) {
ZipEntry entry = (ZipEntry) entries.nextElement();
if(entry.isDirectory())
continue;
String pathname = entry.getName();
if( pathname.endsWith("ZipSelfExtractor.class")
|| innerClassName.equals(pathname)
|| MANIFEST.equals(pathname.toUpperCase())
|| pathname.startsWith(I18N)
|| ((! getInstallOpenSource()) && pathname.matches(OSS_PATTERN))) {
continue;
}
extracted ++;
if ( extracted % FILES_PER_DOT == 0 ) {
System.out.print(".");
}
in = zf.getInputStream(entry);
File outFile = new File(installDir, pathname);
Date archiveTime = new Date(entry.getTime());
if(overwrite==false) {
if(outFile.exists()) {
int result = overwrite(outFile);
if(result == NO) {
continue;
} else if(result == YES_TO_ALL) {
overwrite = true;
} else if(result == CANCEL) {
throw new InstallationCancelled(rb.getString("CANCEL_INSTALL"));
} else if(result == YES) {
//no action required
}
}
}
File parent = new File(outFile.getParent());
if (parent != null && !parent.exists()) {
parent.mkdirs();
}
out = new FileOutputStream(outFile);
while (true) {
int nRead = in.read(buf, 0, buf.length);
if (nRead > 0) {
out.write(buf, 0, nRead);
} else {
break;
}
}
out.close();
outFile.setLastModified(archiveTime.getTime());
}
zf.close();
File productDir = getProductDir();
File binDir = new File(productDir, "bin");
setPermissions(binDir.getAbsolutePath());
if ( getInstallOpenSource() ) {
System.out.println();
System.out.println(rb.getString("INSTALLING_OPENSOURCE"));
File libDir = new File(productDir, "lib");
File productArchive = new File(libDir, getProductJarName());
installOpenSource(productArchive);
File ossArchive = new File(getOpenSourceDir(), OSS_JAR);
installOpenSource(ossArchive);
createJars();
cleanUpOpenSourceDir();
}
} catch (IOException ioe) {
setError(ioe);
if(zf!=null) { try { zf.close(); } catch(IOException ignore) {;} }
if(out!=null) { try {out.close();} catch(IOException ignore) {;} }
if(in!=null) { try { in.close(); } catch(IOException ignore) {;} }
throw new RuntimeException(rb.getString("INPUT_ERROR"), ioe);
}
System.out.println("");
System.out.println(rb.getString("INSTALLATION_COMPLETE"));
}
private void displayEULA() {
if (isInteractive) {
ClassLoader loader = this.getClass().getClassLoader();
BufferedReader stream = null;
stream = new BufferedReader(new InputStreamReader(loader.getResourceAsStream(EULA)));
String line = null;
int count = 20;
try {
while (true) {
if ( count <= 0 ) {
System.out.println("");
System.out.println(rb.getString("CONTINUE"));
System.out.flush();
System.in.read();
count = 20;
}
line = stream.readLine();
count--;
if (line != null) {
System.out.println(line);
} else {
break;
}
}
stream.close();
} catch (IOException ioe) {
setError(ioe);
throw new RuntimeException(rb.getString("EULA_ERROR"), ioe);
}
String response = "";
final String agree = rb.getString("AGREE").toUpperCase();
final String disagree = rb.getString("DISAGREE").toUpperCase();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
boolean flag = false;
while(! response.trim().toUpperCase().startsWith(agree)) {
if ( flag ) {
System.out.println(rb.getString("BE_LITERAL"));
}
flag = true;
if ( response.trim().toUpperCase().startsWith(disagree) ) {
throw new InstallationCancelled(rb.getString("CANCEL_INSTALL"));
}
System.out.println("");
System.out.println(rb.getString("AGREE_TO_EULA"));
try {
response = br.readLine();
} catch (IOException ioe) {
setError(ioe);
throw new RuntimeException(rb.getString("INPUT_ERROR"), ioe);
}
}
}
}
private void queryInstallDir() {
boolean directoryIsChoosen = false;
if (! isInteractive) {
directoryIsChoosen = true;
validateInstallDir();
}
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String response = "";
while (! directoryIsChoosen) {
System.out.println("");
System.out.println(rb.getString("CHOOSE_AN_INSTALL_DIRECTORY"));
String path;
try {
path = installDir.getCanonicalPath();
} catch (IOException ioe) {
path = installDir.getPath();
}
System.out.println("[" + path + "]");
try {
response = br.readLine();
if(! response.trim().equals("")) {
installDir = new File(response.trim());
}
} catch (IOException ioe) {
setError(ioe);
throw new RuntimeException(rb.getString("INPUT_ERROR"), ioe);
}
directoryIsChoosen = validateInstallDir();
}
}
private boolean validateInstallDir() {
String path;
try {
path = installDir.getCanonicalPath();
} catch (IOException ioe) {
path = installDir.getAbsolutePath();
}
if (installDir.exists()) {
System.out.print(rb.getString("ACCEPT_INSTALL_DIRECTORY") + " ");
System.out.println(path + " [" + rb.getString("YES") + "]");
} else {
System.out.println(rb.getString("DIRECTORY_DOES_NOT_EXIST_ACCEPT")
+ " [" + rb.getString("YES") + "]");
}
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String response = "";
try {
if (isInteractive) {
response = br.readLine();
}
response = response.trim().toUpperCase();
final String yes = rb.getString("YES").toUpperCase();
if( response.equals("")
|| response.startsWith(yes)) {
if (! installDir.exists()) {
if (! installDir.mkdirs()) {
if (isInteractive) {
System.out.println(rb.getString("FAILED_TO_CREATE_DIRECTORY"));
return false;
} else {
IOException ioe = new IOException(
rb.getString("FAILED_TO_CREATE_DIRECTORY"));
throw new InstallationCancelled(
rb.getString("CANCEL_INSTALL"), ioe);
}
}
}
if (! installDir.canWrite()) {
if (isInteractive) {
System.out.println(rb.getString("DIRECTORY_LACKS_PERMISSIONS"));
return false;
} else {
IOException ioe = new IOException(rb.getString("DIRECTORY_LACKS_PERMISSIONS"));
throw new InstallationCancelled(rb.getString("CANCEL_INSTALL"), ioe);
}
}
return true;
}
} catch (IOException ioe) {
setError(ioe);
throw new RuntimeException(rb.getString("INPUT_ERROR"), ioe);
}
return false;
}
private int overwrite(File outFile) {
if (! isInteractive) {
String msg = outFile.getPath() + ": " + rb.getString("FILE_CONFLICT");
IOException ioe = new IOException(msg);
throw new InstallationCancelled(rb.getString("CANCEL_INSTALL"), ioe);
//play it safe and exit if the files exist
}
System.out.println("\n");
System.out.println(rb.getString("FILE_CONFLICT"));
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String response;
while(true) {
System.out.println(outFile.getPath() + ":\n"
+ rb.getString("OVERRWRITE"));
try {
response = br.readLine();
} catch (IOException ioe) {
setError(ioe);
throw new RuntimeException(rb.getString("INPUT_ERROR"), ioe);
}
try {
int choice = Integer.parseInt(response);
if ( choice == YES
|| choice == YES_TO_ALL
|| choice == NO
|| choice == CANCEL) {
return choice;
}
} catch( NumberFormatException ignore ) {
}
}
}
private void cleanUpOpenSourceDir() throws IOException {
File openSourceDir = getOpenSourceDir();
File[] files = openSourceDir.listFiles();
// files should never be null because of the lib directory
for(int i=0; i < files.length; i++) {
String name = files[i].getName();
if( files[i].isDirectory()
&& (! (name.equals("lib") || name.equals("source")))) {
deleteDirectory(files[i]);
} else if( name.equals(OSS_JAR)) {
files[i].delete();
}
}
}
protected void createJar(String jarName, String pattern)
throws IOException
{
File openSourceDir = getOpenSourceDir();
File openSourceLibDir = new File(getOpenSourceDir(), "lib");
openSourceLibDir.mkdir();
File jarFile = new File(openSourceLibDir.getPath(), jarName);
JarOutputStream jar = new JarOutputStream (
new FileOutputStream(jarFile), new Manifest());
jarFileTree(jar, openSourceDir, openSourceDir, pattern);
jar.close();
}
private void jarFileTree (
JarOutputStream jar,
File file,
File openSourceDir,
String pattern ) throws FileNotFoundException, IOException
{
if ( file.isDirectory() ) {
File[] search = file.listFiles();
if ( search == null ) {
return; //empty directory
}
for( int i=0; i < search.length; i++) {
jarFileTree(jar, search[i], openSourceDir, pattern);
}
} else {
String path = file.getPath();
if (! path.matches(pattern)) {
return; //Not in our package of interest
}
byte[] buffer = new byte[1024];
int count;
//File sourceFile = new File(openSourceDir, file.getPath());
InputStream is = new BufferedInputStream(new FileInputStream(file));
int index = openSourceDir.getPath().length() + 1;
String entryName = file.getPath().substring(index);
JarEntry entry = new JarEntry(entryName);
jar.putNextEntry(entry);
// Read the file the file and write it to the jar.
while((count = is.read(buffer)) != -1) {
jar.write ( buffer, 0, count ) ;
}
}
}
private File getProductDir() throws FileNotFoundException {
File productDir = new File(installDir, rb.getString("PRODUCT_DIR"));
if ( ! productDir.exists() || ! productDir.isDirectory() ) {
throw new FileNotFoundException(rb.getString("PRODUCT_DIR_NOT_FOUND"));
}
return productDir;
}
private File getOpenSourceDir() throws FileNotFoundException {
return new File(getProductDir(), "opensource");
}
private void installOpenSource(File currentArchive) throws IOException {
File openSourceDir = getOpenSourceDir();
byte[] buf = new byte[1024];
ZipFile zf = null;
FileOutputStream out = null;
InputStream in = null;
try {
zf = new ZipFile(currentArchive);
int size = zf.size();
int extracted = 0;
System.out.println("");
System.out.print(rb.getString("UNZIPPING_FILES"));
Enumeration entries = zf.entries();
for (int i=0; i<size; i++) {
ZipEntry entry = (ZipEntry) entries.nextElement();
if(entry.isDirectory())
continue;
String pathname = entry.getName();
extracted ++;
if ( extracted % FILES_PER_DOT == 0 ) {
System.out.print(".");
}
in = zf.getInputStream(entry);
File outFile = new File(openSourceDir, pathname);
Date archiveTime = new Date(entry.getTime());
File parent = new File(outFile.getParent());
if (parent != null && !parent.exists()) {
parent.mkdirs();
}
out = new FileOutputStream(outFile);
while (true) {
int nRead = in.read(buf, 0, buf.length);
if (nRead > 0) {
out.write(buf, 0, nRead);
} else {
break;
}
}
out.close();
outFile.setLastModified(archiveTime.getTime());
}
zf.close();
} catch (IOException ioe) {
setError(ioe);
if(zf!=null) { try { zf.close(); } catch(IOException ignore) {;} }
if(out!=null) { try {out.close();} catch(IOException ignore) {;} }
if(in!=null) { try { in.close(); } catch(IOException ignore) {;} }
throw new RuntimeException(rb.getString("INPUT_ERROR"), ioe);
}
}
static private boolean deleteDirectory(File path) {
if( path.exists() ) {
File[] files = path.listFiles();
if ( files == null ) {
return( path.delete() ); //empty directory
}
for(int i=0; i<files.length; i++) {
if(files[i].isDirectory()) {
deleteDirectory(files[i]);
} else {
files[i].delete();
}
}
}
return( path.delete() );
}
protected void installProduct() throws Throwable {
try {
String jarFileName = getJarFileName();
extract(jarFileName);
} catch( Throwable t ) {
setError(t);
} finally {
handleFailure();
}
}
static class InstallationCancelled extends RuntimeException {
private static final long serialVersionUID = 7801293643025052283L;
public InstallationCancelled() {super();}
public InstallationCancelled(String msg) {super(msg);}
public InstallationCancelled(String msg, Throwable cause) {
super(msg, cause);
}
}
private static void addFiles(String binDir, List command) {
File dir = new File(binDir);
File[] children = dir.listFiles();
if (children == null) {
// Either dir does not exist or is not a directory
} else {
for (int i=0; i<children.length; i++) {
if ( children[i].isFile() ) {
command.add(children[i].getAbsolutePath());
}
}
}
}
private void setPermissions(String binDir) {
String OS = System.getProperty("os.name", "UNSET").toLowerCase();
final List command = new ArrayList();
if ( OS.indexOf("windows") == -1 ) {
//not on Windows, assume chmod
command.add("chmod");
command.add("755");
addFiles(binDir, command);
} else {
/*
command.add("cmd.exe");
command.add("/c");
command.add("cacls.exe");
addFiles(binDir, command);
command.add("/E");
command.add("/C");
command.add("/E");
command.add("/E");
*/
//Do nothing on Windows for now
return;
}
String[] cmd = new String[command.size()];
command.toArray(cmd);
int retVal = -1;
Process p = null;
try {
p = Runtime.getRuntime().exec ( cmd );
retVal = p.waitFor();
} catch (Exception suppress) {
} finally {
if(p != null) p.destroy();
}
if ( retVal != 0 ) {
System.out.println(rb.getString("FAIL_TO_SET_PERMISSIONS"));
}
}
}