/*******************************************************************************
* Copyright (c) Dec 7, 2011 Zend Technologies Ltd.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.zend.sdkcli.update.parser;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.zend.sdkcli.monitor.ProgressMonitor;
import org.zend.sdkcli.monitor.TextProgressMonitor;
import org.zend.sdkcli.update.UpdateException;
import org.zend.sdkcli.update.monitor.UpdateOutputStream;
/**
*
* Represents group of actions which should be performed to update Zend SDK. It
* is responsible for downloading and extracting update package and execution of
* all entries defined in it.
*
* @author Wojciech Galanciak, 2011
*
*/
public class Delta {
private static final String REMOVE = "remove";
private static final String ADD = "add";
private static final int BUFFER = 4096;
private List<AbstractDeltaEntry> entries;
private File temp;
private String zipLocation;
private int size;
private ProgressMonitor monitor;
public Delta(Document doc, String zipLocation, int size) {
this.zipLocation = zipLocation;
this.size = size;
this.entries = new ArrayList<AbstractDeltaEntry>();
this.temp = getTempFile();
this.monitor = new TextProgressMonitor(new PrintWriter(System.out));
parse(doc);
}
/**
* @return temporary file for all files used during update
*/
public File getTemp() {
return temp;
}
/**
* @return location of update package defined in delta.xml
*/
public String getZipLocation() {
return zipLocation;
}
/**
* Executes update process based on delta defined in delta.xml file.
*
* @param root
* - root file of Zend SDK
* @return true if execution was performed successfully; otherwise returns
* false
* @throws UpdateException
*/
public boolean execute(File root) throws UpdateException {
extractZip(getZipFile(getZipLocation()));
for (AbstractDeltaEntry entry : entries) {
if (!entry.execute(root)) {
return false;
}
}
return true;
}
private void parse(Document doc) {
NodeList removes = doc.getElementsByTagName(REMOVE);
for (int i = 0; i < removes.getLength(); i++) {
entries.add(new RemoveEntry(removes.item(i)));
}
NodeList adds = doc.getElementsByTagName(ADD);
for (int i = 0; i < adds.getLength(); i++) {
entries.add(new AddEntry(adds.item(i), getTemp()));
}
}
private File getZipFile(String location) throws UpdateException {
monitor.beginTask("Downloading update package", size);
String fileName = location.substring(location.replace("\\", "/")
.lastIndexOf('/') + 1);
File result = new File(getTemp(), fileName);
BufferedInputStream in = null;
FileOutputStream out = null;
try {
URL url = new URL(location);
try {
in = new BufferedInputStream(url.openStream());
out = new UpdateOutputStream(result, monitor, size);
byte data[] = new byte[BUFFER];
int count;
while ((count = in.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
} finally {
closeStream(in);
closeStream(out);
}
} catch (MalformedURLException e) {
throw new UpdateException(e);
} catch (IOException e) {
throw new UpdateException(e);
}
return result;
}
private void extractZip(File zipFile) throws UpdateException {
try {
monitor.beginTask("Extracting update package",
new ZipFile(zipFile).size());
BufferedOutputStream dest = null;
FileInputStream in = new FileInputStream(zipFile);
File parent = zipFile.getParentFile();
ZipInputStream zipStream = new ZipInputStream(
new BufferedInputStream(in));
ZipEntry entry;
while ((entry = zipStream.getNextEntry()) != null) {
int count;
byte data[] = new byte[BUFFER];
File file = new File(parent, entry.getName());
createParents(file.getParentFile(), parent);
if (entry.getName().endsWith("/")) {
file.mkdir();
} else {
file.getParentFile().mkdirs();
file.createNewFile();
FileOutputStream out = new FileOutputStream(file);
dest = new BufferedOutputStream(out, BUFFER);
while ((count = zipStream.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, count);
}
dest.flush();
closeStream(dest);
}
monitor.update(1);
}
closeStream(zipStream);
} catch (Exception e) {
throw new UpdateException(e);
}
monitor.endTask();
}
private File getTempFile() {
String tempDir = System.getProperty("java.io.tmpdir");
File file = new File(tempDir + File.separator + new Random().nextInt());
file.mkdir();
return file;
}
private boolean createParents(File file, File root) {
File parent = file.getParentFile();
if (parent.equals(root)) {
file.mkdir();
return true;
} else {
if (!parent.exists()) {
if (createParents(parent, root)) {
file.mkdir();
return true;
}
}
}
return true;
}
private void closeStream(Closeable stream) throws IOException {
if (stream != null) {
stream.close();
}
}
}