Skip to content

Commit

Permalink
Started working on #9
Browse files Browse the repository at this point in the history
  • Loading branch information
edvin committed May 6, 2016
1 parent 83c27a5 commit 9c30020
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 63 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# Change Log
All notable changes to this project will be documented in this file.

## [Unreleased]
## [1.0.10]

### Changed

- Add optional `--cache-dir` program parameter `cacheDir` and manifest entry (https://github.com/edvin/fxlauncher/issues/9)
- Add / if missing from base url (https://github.com/edvin/fxlauncher/issues/6)

## [1.0.9] - 2016-03-14
Expand Down
140 changes: 94 additions & 46 deletions src/main/java/fxlauncher/FXManifest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,25 @@
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@SuppressWarnings("unchecked")
@XmlRootElement(name = "Application")
public class FXManifest {
@XmlAttribute
URI uri;
@XmlAttribute(name = "launch")
String launchClass;
@XmlElement(name = "lib")
List<LibraryFile> files = new ArrayList<>();
@XmlAttribute
URI uri;
@XmlAttribute(name = "launch")
String launchClass;
@XmlElement(name = "lib")
List<LibraryFile> files = new ArrayList<>();
@XmlElement
String updateText = "Updating...";
@XmlElement
Expand All @@ -26,50 +30,94 @@ public class FXManifest {
String progressBarStyle = "-fx-pref-width: 200;";
@XmlElement
String wrapperStyle = "-fx-spacing: 10; -fx-padding: 25;";
@XmlElement
String parameters;
@XmlElement
String parameters;
@XmlElement
String cacheDir;

public String getFilename() {
return String.format("%s.xml", launchClass);
}

public URI getFXAppURI() {
if (uri.getPath().endsWith("/"))
return uri.resolve("app.xml");

return URI.create(uri.toString() + "/app.xml");
}

public Path getPath(Path cacheDir) {
return cacheDir.resolve(getFilename());
}

public Path resolveCacheDir(Map<String, String> namedParams) {
if (namedParams == null) namedParams = Collections.emptyMap();

public String getFilename() {
return String.format("%s.xml", launchClass);
}
String cacheDir = namedParams.containsKey("cache-dir") ? namedParams.get("cache-dir") : this.cacheDir;

public URI getFXAppURI() {
if (uri.getPath().endsWith("/"))
return uri.resolve("app.xml");
if (cacheDir == null || cacheDir.isEmpty()) return Paths.get(".");

return URI.create(uri.toString() + "/app.xml");
}
Path path;

if (cacheDir.startsWith("USERLIB/")) {
switch (OS.current) {
case mac:
path = Paths.get(System.getProperty("user.home"))
.resolve("Library")
.resolve("Application Support")
.resolve(cacheDir.substring(8));
break;
case win:
path = Paths.get(System.getProperty("user.home"))
.resolve("AppData")
.resolve(cacheDir.substring(8));
break;
default:
path = Paths.get(System.getProperty("user.home"))
.resolve("." + cacheDir.substring(8));
}
} else {
path = Paths.get(cacheDir);
}

if (!Files.exists(path)) {
try {
Files.createDirectories(path);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

return Paths.get(cacheDir);
}

public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

FXManifest that = (FXManifest) o;

if (uri != null ? !uri.equals(that.uri) : that.uri != null) return false;
if (launchClass != null ? !launchClass.equals(that.launchClass) : that.launchClass != null) return false;
if (files != null ? !files.equals(that.files) : that.files != null) return false;
if (updateText != null ? !updateText.equals(that.updateText) : that.updateText != null) return false;
if (updateLabelStyle != null ? !updateLabelStyle.equals(that.updateLabelStyle) : that.updateLabelStyle != null)
return false;
if (progressBarStyle != null ? !progressBarStyle.equals(that.progressBarStyle) : that.progressBarStyle != null)
return false;
return wrapperStyle != null ? wrapperStyle.equals(that.wrapperStyle) : that.wrapperStyle == null;

}

public Path getPath() {
return Paths.get(getFilename());
public int hashCode() {
int result = uri != null ? uri.hashCode() : 0;
result = 31 * result + (launchClass != null ? launchClass.hashCode() : 0);
result = 31 * result + (files != null ? files.hashCode() : 0);
result = 31 * result + (updateText != null ? updateText.hashCode() : 0);
result = 31 * result + (updateLabelStyle != null ? updateLabelStyle.hashCode() : 0);
result = 31 * result + (progressBarStyle != null ? progressBarStyle.hashCode() : 0);
result = 31 * result + (wrapperStyle != null ? wrapperStyle.hashCode() : 0);
return result;
}

public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

FXManifest that = (FXManifest) o;

if (uri != null ? !uri.equals(that.uri) : that.uri != null) return false;
if (launchClass != null ? !launchClass.equals(that.launchClass) : that.launchClass != null) return false;
if (files != null ? !files.equals(that.files) : that.files != null) return false;
if (updateText != null ? !updateText.equals(that.updateText) : that.updateText != null) return false;
if (updateLabelStyle != null ? !updateLabelStyle.equals(that.updateLabelStyle) : that.updateLabelStyle != null)
return false;
if (progressBarStyle != null ? !progressBarStyle.equals(that.progressBarStyle) : that.progressBarStyle != null)
return false;
return wrapperStyle != null ? wrapperStyle.equals(that.wrapperStyle) : that.wrapperStyle == null;

}

public int hashCode() {
int result = uri != null ? uri.hashCode() : 0;
result = 31 * result + (launchClass != null ? launchClass.hashCode() : 0);
result = 31 * result + (files != null ? files.hashCode() : 0);
result = 31 * result + (updateText != null ? updateText.hashCode() : 0);
result = 31 * result + (updateLabelStyle != null ? updateLabelStyle.hashCode() : 0);
result = 31 * result + (progressBarStyle != null ? progressBarStyle.hashCode() : 0);
result = 31 * result + (wrapperStyle != null ? wrapperStyle.hashCode() : 0);
return result;
}
}
27 changes: 16 additions & 11 deletions src/main/java/fxlauncher/Launcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
Expand Down Expand Up @@ -58,7 +57,8 @@ public void start(Stage primaryStage) throws Exception {
try {
updateManifest();
createUpdateWrapper();
syncFiles();
Path cacheDir = manifest.resolveCacheDir(getParameters().getNamed());
syncFiles(cacheDir);
} catch (Exception ex) {
log.log(Level.WARNING, String.format("Error during %s phase", phase), ex);
}
Expand Down Expand Up @@ -95,10 +95,10 @@ private void createUpdateWrapper() {
});
}

public URLClassLoader createClassLoader() {
private URLClassLoader createClassLoader(Path cacheDir) {
List<URL> libs = manifest.files.stream()
.filter(LibraryFile::loadForCurrentPlatform)
.map(LibraryFile::toURL)
.map(it -> it.toURL(cacheDir))
.collect(Collectors.toList());

return new URLClassLoader(libs.toArray(new URL[libs.size()]));
Expand All @@ -124,19 +124,19 @@ private void updateManifest() throws Exception {
syncManifest();
}

private void syncFiles() throws Exception {
private void syncFiles(Path cacheDir) throws Exception {
phase = "File Synchronization";

List<LibraryFile> needsUpdate = manifest.files.stream()
.filter(LibraryFile::loadForCurrentPlatform)
.filter(LibraryFile::needsUpdate)
.filter(it -> it.needsUpdate(cacheDir))
.collect(Collectors.toList());

Long totalBytes = needsUpdate.stream().mapToLong(f -> f.size).sum();
Long totalWritten = 0L;

for (LibraryFile lib : needsUpdate) {
Path target = Paths.get(lib.file).toAbsolutePath();
Path target = cacheDir.resolve(lib.file).toAbsolutePath();
Files.createDirectories(target.getParent());

try (InputStream input = manifest.uri.resolve(lib.file).toURL().openStream();
Expand All @@ -158,7 +158,9 @@ private void syncFiles() throws Exception {
private void createApplication() throws Exception {
phase = "Create Application";

URLClassLoader classLoader = createClassLoader();
Path cacheDir = manifest.resolveCacheDir(getParameters().getNamed());

URLClassLoader classLoader = createClassLoader(cacheDir);
FXMLLoader.setDefaultClassLoader(classLoader);
Thread.currentThread().setContextClassLoader(classLoader);
Platform.runLater(() -> Thread.currentThread().setContextClassLoader(classLoader));
Expand Down Expand Up @@ -205,8 +207,11 @@ private void syncManifest() throws Exception {
URL embeddedManifest = Launcher.class.getResource("/app.xml");
manifest = JAXB.unmarshal(embeddedManifest, FXManifest.class);

if (Files.exists(manifest.getPath()))
manifest = JAXB.unmarshal(manifest.getPath().toFile(), FXManifest.class);
Path cacheDir = manifest.resolveCacheDir(namedParams);
Path manifestPath = manifest.getPath(cacheDir);

if (Files.exists(manifestPath))
manifest = JAXB.unmarshal(manifestPath.toFile(), FXManifest.class);

try {
FXManifest remoteManifest = JAXB.unmarshal(manifest.getFXAppURI(), FXManifest.class);
Expand All @@ -215,7 +220,7 @@ private void syncManifest() throws Exception {
log.info(String.format("No remote manifest at %s", manifest.getFXAppURI()));
} else if (!remoteManifest.equals(manifest)) {
manifest = remoteManifest;
JAXB.marshal(manifest, manifest.getPath().toFile());
JAXB.marshal(manifest, manifestPath.toFile());
}
} catch (Exception ex) {
log.log(Level.WARNING, "Unable to update manifest", ex);
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/fxlauncher/LibraryFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ public class LibraryFile {
@XmlAttribute
OS os;

public boolean needsUpdate() {
Path path = Paths.get(file);
public boolean needsUpdate(Path cacheDir) {
Path path = cacheDir.resolve(file);
try {
return !Files.exists(path) || Files.size(path) != size || checksum(path) != checksum;
} catch (IOException e) {
Expand All @@ -50,15 +50,15 @@ public boolean loadForCurrentPlatform() {
return os == null || os == OS.current;
}

public URL toURL() {
public URL toURL(Path cacheDir) {
try {
return Paths.get(file).toFile().toURI().toURL();
return cacheDir.resolve(file).toFile().toURI().toURL();
} catch (MalformedURLException whaat) {
throw new RuntimeException(whaat);
}
}

public static long checksum(Path path) throws IOException {
private static long checksum(Path path) throws IOException {
try (InputStream input = Files.newInputStream(path)) {
Adler32 checksum = new Adler32();
byte[] buf = new byte[16384];
Expand Down

0 comments on commit 9c30020

Please sign in to comment.