Skip to content

Commit 3966840

Browse files
authored
Merge pull request #134 from shannah/dedicated-jre
Dedicated jre for Java8 on windows
2 parents 68d68f7 + 2e6bdc6 commit 3966840

35 files changed

+1154
-255
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ installer/jdeploy-bundle
1212
cli/bin
1313
tests/projects/*/jdeploy
1414
./jdeploy
15+
#./installer/tests/*/mock_launcher*

installer/src/main/java/ca/weblite/jdeploy/installer/DefaultInstallationContext.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,12 @@ public File findInstallFilesDir() {
6767
}
6868

6969
System.out.println("Found client4.launcher.path property: "+launcherPath);
70-
cachedInstallFilesDir = findInstallFilesDir(new File(launcherPath));
70+
if (System.getProperty("client4j.appxml.path") != null) {
71+
cachedInstallFilesDir = findInstallFilesDir(new File(System.getProperty("client4j.appxml.path")).getParentFile());
72+
} else {
73+
cachedInstallFilesDir = findInstallFilesDir(new File(launcherPath));
74+
}
75+
7176
return cachedInstallFilesDir;
7277
} else {
7378
System.out.println("client4j.launcher.path is not set");
@@ -261,6 +266,9 @@ private String getDefaultWebsiteUrl(String packageName, String source) throws Un
261266

262267

263268
public File findAppXml() {
269+
if (System.getProperty("client4j.appxml.path") != null) {
270+
return new File(System.getProperty("client4j.appxml.path"));
271+
}
264272
File installFilesDir = findInstallFilesDir();
265273
if (installFilesDir == null) {
266274
return null;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package ca.weblite.jdeploy.installer;
2+
3+
import ca.weblite.jdeploy.installer.models.InstallationSettings;
4+
5+
public class HeadlessInstallationSettings extends InstallationSettings {
6+
7+
}

installer/src/main/java/ca/weblite/jdeploy/installer/Main.java

+72-235
Large diffs are not rendered by default.

installer/src/main/java/ca/weblite/jdeploy/installer/npm/NPMPackageVersion.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.ArrayList;
1010

1111
public class NPMPackageVersion {
12+
private static final String DEFAULT_JAVA_VERSION = "11";
1213
private final NPMPackage npmPackage;
1314
private final String version;
1415
private final JSONObject packageJson;
@@ -36,7 +37,12 @@ public String getVersion() {
3637
return version;
3738
}
3839

39-
40+
public String getJavaVersion() {
41+
if (jdeploy().has("javaVersion")) {
42+
return jdeploy().getString("javaVersion");
43+
}
44+
return DEFAULT_JAVA_VERSION;
45+
}
4046

4147
private JSONObject jdeploy() {
4248
return packageJson.getJSONObject("jdeploy");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
package ca.weblite.jdeploy.installer.win;
2+
3+
import ca.weblite.jdeploy.app.AppInfo;
4+
import ca.weblite.jdeploy.installer.InstallationContext;
5+
import ca.weblite.jdeploy.installer.Main;
6+
import ca.weblite.jdeploy.installer.models.InstallationSettings;
7+
import ca.weblite.tools.io.FileUtil;
8+
import com.izforge.izpack.util.os.ShellLink;
9+
import net.coobird.thumbnailator.Thumbnails;
10+
import net.sf.image4j.codec.ico.ICOEncoder;
11+
import org.apache.commons.io.FileUtils;
12+
13+
import java.awt.image.BufferedImage;
14+
import java.io.File;
15+
import java.io.FileOutputStream;
16+
import java.io.IOException;
17+
import java.nio.file.Files;
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
21+
import static ca.weblite.tools.io.IOUtil.copyResourceToFile;
22+
23+
public class InstallWindows {
24+
25+
public File install(
26+
InstallationContext context,
27+
InstallationSettings installationSettings,
28+
String fullyQualifiedPackageName,
29+
File tmpBundles
30+
) throws Exception {
31+
AppInfo appInfo = installationSettings.getAppInfo();
32+
File tmpExePath = findTmpExeFile(tmpBundles);
33+
File appXmlFile = context.findAppXml();
34+
File userHome = new File(System.getProperty("user.home"));
35+
File jdeployHome = new File(userHome, ".jdeploy");
36+
File appsDir = new File(jdeployHome, "apps");
37+
File appDir = new File(appsDir, fullyQualifiedPackageName);
38+
appDir.mkdirs();
39+
String nameSuffix = "";
40+
41+
if (appInfo.getNpmVersion().startsWith("0.0.0-")) {
42+
nameSuffix = " " +
43+
appInfo.getNpmVersion().substring(appInfo.getNpmVersion().indexOf("-") + 1)
44+
.trim();
45+
}
46+
47+
String exeName = appInfo.getTitle() + nameSuffix + ".exe";
48+
File exePath = new File(appDir, exeName);
49+
if (appInfo.isUsePrivateJVM()) {
50+
File appBinDir = new File(appDir, "bin");
51+
appBinDir.mkdirs();
52+
exePath = new File(appBinDir, exeName);
53+
}
54+
55+
FileUtil.copy(tmpExePath, exePath);
56+
exePath.setExecutable(true, false);
57+
58+
// Copy the icon.png if it is present
59+
File bundleIcon = new File(appXmlFile.getParentFile(), "icon.png");
60+
File iconPath = new File(appDir, "icon.png");
61+
File icoPath = new File(appDir, "icon.ico");
62+
63+
if (Files.exists(bundleIcon.toPath())) {
64+
FileUtil.copy(bundleIcon, iconPath);
65+
}
66+
installWindowsLinks(
67+
appInfo,
68+
installationSettings,
69+
exePath,
70+
appDir,
71+
appInfo.getTitle() + nameSuffix
72+
);
73+
74+
File registryBackupLogs = new File(appDir, "registry-backup-logs");
75+
76+
if (!registryBackupLogs.exists()) {
77+
registryBackupLogs.mkdirs();
78+
}
79+
int nextLogIndex = 0;
80+
for (File child : registryBackupLogs.listFiles()) {
81+
if (child.getName().endsWith(".reg")) {
82+
String logIndex = child.getName().substring(0, child.getName().lastIndexOf("."));
83+
try {
84+
int logIndexInt = Integer.parseInt(logIndex);
85+
if (logIndexInt >= nextLogIndex) {
86+
nextLogIndex = logIndexInt+1;
87+
}
88+
} catch (Exception ex) {}
89+
}
90+
}
91+
File registryBackupLog = new File(registryBackupLogs, nextLogIndex+".reg");
92+
try (FileOutputStream fos = new FileOutputStream(registryBackupLog)) {
93+
InstallWindowsRegistry registryInstaller = new InstallWindowsRegistry(appInfo, exePath, icoPath, fos);
94+
registryInstaller.register();
95+
96+
//Try to copy the uninstaller
97+
File uninstallerPath = registryInstaller.getUninstallerPath();
98+
uninstallerPath.getParentFile().mkdirs();
99+
File installerExePath = new File(System.getProperty("client4j.launcher.path"));
100+
if (uninstallerPath.exists()) {
101+
if (!uninstallerPath.canWrite()) {
102+
uninstallerPath.setWritable(true, false);
103+
try {
104+
// Give windows time to update its cache
105+
Thread.sleep(1000L);
106+
} catch (InterruptedException interruptedException) {
107+
// Ignore
108+
}
109+
}
110+
if (!uninstallerPath.delete()) {
111+
throw new IOException("Failed to delete uninstaller: "+uninstallerPath);
112+
}
113+
114+
try {
115+
// Give Windows time to update its cache
116+
Thread.sleep(1000L);
117+
} catch (InterruptedException interruptedException) {
118+
// Ignore
119+
}
120+
}
121+
FileUtils.copyFile(installerExePath, uninstallerPath);
122+
uninstallerPath.setExecutable(true, false);
123+
FileUtils.copyDirectory(
124+
context.findInstallFilesDir(),
125+
new File(
126+
uninstallerPath.getParentFile(),
127+
context.findInstallFilesDir().getName()
128+
)
129+
);
130+
131+
return exePath;
132+
133+
} catch (Exception ex) {
134+
// restore
135+
try {
136+
InstallWindowsRegistry.rollback(registryBackupLog);
137+
} catch (Exception rollbackException) {
138+
throw new RuntimeException(
139+
"Failed to roll back registry after failed installation.",
140+
rollbackException
141+
);
142+
}
143+
// Since we rolled back the changes, we'll delete the backup log so that it doesn't get rolled back again.
144+
registryBackupLog.delete();
145+
ex.printStackTrace(System.err);
146+
throw new RuntimeException("Failed to update registry. Rolling back changes.", ex);
147+
148+
}
149+
}
150+
151+
private void installWindowsLink(
152+
AppInfo appInfo,
153+
int type,
154+
File exePath,
155+
File iconPath,
156+
String appTitle
157+
) throws Exception {
158+
ShellLink link = new ShellLink(type, appTitle);
159+
link.setUserType(ShellLink.CURRENT_USER);
160+
if (appInfo.getDescription() == null) {
161+
link.setDescription("Windows application");
162+
} else {
163+
link.setDescription(appInfo.getDescription());
164+
}
165+
String iconPathString = iconPath.getCanonicalFile().getAbsolutePath();
166+
link.setIconLocation(iconPathString, 0);
167+
String exePathString = exePath.getCanonicalFile().getAbsolutePath();
168+
link.setTargetPath(exePathString);
169+
link.save();
170+
}
171+
172+
private void installWindowsLinks(
173+
AppInfo appInfo,
174+
InstallationSettings installationSettings,
175+
File exePath,
176+
File appDir,
177+
String appTitle
178+
) throws Exception {
179+
File pngIconPath = new File(appDir, "icon.png");
180+
File icoPath = new File(appDir.getCanonicalFile(), "icon.ico");
181+
182+
if (!Files.exists(pngIconPath.toPath())) {
183+
copyResourceToFile(Main.class, "icon.png", pngIconPath);
184+
}
185+
if (!Files.exists(pngIconPath.toPath())) {
186+
throw new IOException("Failed to create the .ico file for some reason. "+icoPath);
187+
}
188+
convertWindowsIcon(pngIconPath.getCanonicalFile(), icoPath);
189+
190+
191+
if (installationSettings.isAddToDesktop()) {
192+
try {
193+
installWindowsLink(appInfo, ShellLink.DESKTOP, exePath, icoPath, appTitle);
194+
} catch (Exception ex) {
195+
throw new RuntimeException("Failed to install desktop shortcut", ex);
196+
197+
}
198+
}
199+
if (installationSettings.isAddToPrograms()) {
200+
try {
201+
installWindowsLink(appInfo, ShellLink.PROGRAM_MENU, exePath, icoPath, appTitle);
202+
} catch (Exception ex) {
203+
throw new RuntimeException("Failed to install program menu shortcut", ex);
204+
205+
}
206+
}
207+
if (installationSettings.isAddToStartMenu()) {
208+
try {
209+
installWindowsLink(appInfo, ShellLink.START_MENU, exePath, icoPath, appTitle);
210+
} catch (Exception ex) {
211+
throw new RuntimeException("Failed to install start menu shortcut", ex);
212+
213+
}
214+
}
215+
}
216+
217+
private void convertWindowsIcon(File srcPng, File destIco) throws IOException {
218+
List<BufferedImage> images = new ArrayList<>();
219+
List<Integer> bppList = new ArrayList<>();
220+
for (int i : new int[]{16, 24, 32, 48, 64, 128, 256}) {
221+
BufferedImage img = Thumbnails.of(srcPng).size(i, i).asBufferedImage();
222+
images.add(img);
223+
bppList.add(32);
224+
if (i <= 48) {
225+
images.add(img);
226+
bppList.add(8);
227+
images.add(img);
228+
bppList.add(4);
229+
}
230+
}
231+
int[] bppArray = bppList.stream().mapToInt(i->i).toArray();
232+
try (FileOutputStream fileOutputStream = new FileOutputStream(destIco)) {
233+
ICOEncoder.write(images,bppArray, fileOutputStream);
234+
}
235+
}
236+
237+
private File findTmpExeFile(File tmpBundles) {
238+
File tmpExePath = null;
239+
for (File exeCandidate : new File(tmpBundles, "windows").listFiles()) {
240+
if (exeCandidate.getName().endsWith(".exe")) {
241+
tmpExePath = exeCandidate;
242+
}
243+
}
244+
if (tmpExePath == null) {
245+
throw new RuntimeException("Failed to find exe file after creation. Something must have gone wrong in generation process");
246+
}
247+
248+
return tmpExePath;
249+
}
250+
}

installer/src/main/java/ca/weblite/jdeploy/installer/win/InstallWindowsRegistry.java

+28-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ca.weblite.jdeploy.installer.win;
22

33
import ca.weblite.jdeploy.app.AppInfo;
4+
import ca.weblite.jdeploy.installer.models.InstallationSettings;
45
import ca.weblite.tools.io.MD5;
56
import org.apache.commons.io.FileUtils;
67

@@ -190,7 +191,12 @@ public String getFullRegistryPath() {
190191

191192
}
192193

193-
public InstallWindowsRegistry(AppInfo appInfo, File exe, File icon, OutputStream backupLog) {
194+
public InstallWindowsRegistry(
195+
AppInfo appInfo,
196+
File exe,
197+
File icon,
198+
OutputStream backupLog
199+
) {
194200
this.appInfo = appInfo;
195201
this.exe = exe;
196202
this.icon = icon;
@@ -371,7 +377,13 @@ private void registerFileExtension(String ext) {
371377
if (mimetype != null) {
372378
registrySetStringValue(HKEY_CURRENT_USER, key, "ContentType", mimetype);
373379
if (mimetype.contains("/")) {
374-
registrySetStringValue(HKEY_CURRENT_USER, key, "PerceivedType", mimetype.substring(0, mimetype.indexOf("/")));
380+
registrySetStringValue(
381+
HKEY_CURRENT_USER,
382+
key,
383+
"PerceivedType",
384+
mimetype.substring(0, mimetype.indexOf("/")
385+
)
386+
);
375387
}
376388
}
377389
}
@@ -452,7 +464,8 @@ public static void rollback(File backupLog) throws IOException {
452464
Scanner scanner = new Scanner(backupLog, "UTF-8");
453465
while (scanner.hasNextLine()) {
454466
String line = scanner.nextLine();
455-
// If we added a key, we wrote them to the log file as comments of the form ;CREATE key where key is like Software\Classes\...
467+
// If we added a key, we wrote them to the log file as comments of the form
468+
// ;CREATE key where key is like Software\Classes\...
456469
if (line.startsWith(";CREATE ")) {
457470
deleteKeyRecursive(line.substring(line.indexOf(" ")+1));
458471
}
@@ -661,7 +674,12 @@ public void register() throws IOException {
661674
}
662675

663676
// Register the application
664-
registrySetStringValue(HKEY_CURRENT_USER, "Software\\RegisteredApplications", getRegisteredAppName(), getCapabilitiesPath());
677+
registrySetStringValue(
678+
HKEY_CURRENT_USER,
679+
"Software\\RegisteredApplications",
680+
getRegisteredAppName(),
681+
getCapabilitiesPath()
682+
);
665683

666684
// Now to register the uninstaller
667685

@@ -673,7 +691,12 @@ public void register() throws IOException {
673691
registrySetStringValue(HKEY_CURRENT_USER, getUninstallKey(), "DisplayVersion", appInfo.getVersion());
674692
registrySetLongValue(HKEY_CURRENT_USER, getUninstallKey(), 1);
675693
registrySetStringValue(HKEY_CURRENT_USER, getUninstallKey(), "Publisher", appInfo.getVendor());
676-
registrySetStringValue(HKEY_CURRENT_USER, getUninstallKey(), "UninstallString", "\""+getUninstallerPath().getAbsolutePath()+"\" uninstall");
694+
registrySetStringValue(
695+
HKEY_CURRENT_USER,
696+
getUninstallKey(),
697+
"UninstallString",
698+
"\"" + getUninstallerPath().getAbsolutePath() + "\" uninstall"
699+
);
677700

678701
String installerPath = System.getProperty("client4j.launcher.path");
679702
if (installerPath == null) {

0 commit comments

Comments
 (0)