blob: 0123556b4e62665154016bbf672e47856ef19e22 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed 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.jetbrains.plugins.javaFX.packaging;
import com.intellij.execution.CommandLineUtil;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.Base64Converter;
import com.intellij.util.PathUtilRt;
import com.intellij.util.io.ZipUtil;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
/**
* User: anna
* Date: 3/12/13
*/
public abstract class AbstractJavaFxPackager {
private static final Logger LOG = Logger.getInstance("#" + AbstractJavaFxPackager.class.getName());
private static final String JB_JFX_JKS = "jb-jfx.jks";
//artifact description
protected String getArtifactRootName() {
return PathUtilRt.getFileName(getArtifactOutputFilePath());
}
protected abstract String getArtifactName();
protected abstract String getArtifactOutputPath();
protected abstract String getArtifactOutputFilePath();
//artifact properties
protected abstract String getAppClass();
protected abstract String getTitle();
protected abstract String getVendor();
protected abstract String getDescription();
protected abstract String getWidth();
protected abstract String getHeight();
protected abstract String getHtmlParamFile();
protected abstract String getParamFile();
protected abstract String getUpdateMode();
protected abstract JavaFxPackagerConstants.NativeBundles getNativeBundle();
protected abstract void registerJavaFxPackagerError(final String message);
public void buildJavaFxArtifact(final String homePath) {
if (!checkNotEmpty(getAppClass(), "Application class")) return;
if (!checkNotEmpty(getWidth(), "Width")) return;
if (!checkNotEmpty(getHeight(), "Height")) return;
final String zipPath = getArtifactOutputFilePath();
final File tempUnzippedArtifactOutput;
try {
tempUnzippedArtifactOutput = FileUtil.createTempDirectory("artifact", "unzipped");
final File artifactOutputFile = new File(zipPath);
ZipUtil.extract(artifactOutputFile, tempUnzippedArtifactOutput, null);
copyLibraries(FileUtil.getNameWithoutExtension(artifactOutputFile), tempUnzippedArtifactOutput);
}
catch (IOException e) {
registerJavaFxPackagerError(e);
return;
}
final File tempDirectory = new File(tempUnzippedArtifactOutput, "deploy");
try {
final StringBuilder buf = new StringBuilder();
buf.append("<project default=\"build artifact\">\n");
buf.append("<taskdef resource=\"com/sun/javafx/tools/ant/antlib.xml\" uri=\"javafx:com.sun.javafx.tools.ant\" ")
.append("classpath=\"").append(homePath).append("/lib/ant-javafx.jar\"/>\n");
buf.append("<target name=\"build artifact\" xmlns:fx=\"javafx:com.sun.javafx.tools.ant\">");
final String artifactFileName = getArtifactRootName();
final List<JavaFxAntGenerator.SimpleTag> tags =
JavaFxAntGenerator.createJarAndDeployTasks(this, artifactFileName, getArtifactName(), tempUnzippedArtifactOutput.getPath());
for (JavaFxAntGenerator.SimpleTag tag : tags) {
tag.generate(buf);
}
buf.append("</target>");
buf.append("</project>");
final int result = startAntTarget(buf.toString(), homePath);
if (result == 0) {
if (isEnabledSigning()) {
signApp(homePath + File.separator + "bin", tempDirectory);
}
}
else {
registerJavaFxPackagerError("fx:deploy task has failed.");
}
}
finally {
copyResultsToArtifactsOutput(tempDirectory);
FileUtil.delete(tempUnzippedArtifactOutput);
}
}
private void copyLibraries(String zipPath, File tempUnzippedArtifactOutput) throws IOException {
final File[] outFiles = new File(getArtifactOutputPath()).listFiles();
if (outFiles != null) {
final String[] generatedItems = new String[] {JB_JFX_JKS, zipPath + ".jar", zipPath + ".jnlp", zipPath + ".html"};
for (File file : outFiles) {
final String fileName = file.getName();
if (ArrayUtilRt.find(generatedItems, fileName) < 0) {
final File destination = new File(tempUnzippedArtifactOutput, fileName);
if (file.isFile()) {
FileUtil.copy(file, destination);
} else {
FileUtil.copyDir(file, destination, true);
}
}
}
}
}
private boolean checkNotEmpty(final String text, final String title) {
if (StringUtil.isEmptyOrSpaces(text)) {
registerJavaFxPackagerError("Unable to build JavaFX artifact. " + title + " should be specified in artifact's settings.");
return false;
}
return true;
}
private void signApp(String binPath, File tempDirectory) {
final boolean selfSigning = isSelfSigning();
final int genResult = selfSigning ? genKey(binPath) : 0;
if (genResult == 0) {
final File[] files = tempDirectory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isFile() && file.getName().endsWith(".jar")) {
sign(binPath, selfSigning, file.getPath());
}
}
}
} else {
registerJavaFxPackagerError("JavaFX generate certificate task has failed.");
}
}
private void sign(String binPath, boolean selfSigning, final String jar2Sign) {
final List<String> signCommandLine = new ArrayList<String>();
addParameter(signCommandLine, FileUtil.toSystemDependentName(binPath + File.separator + "jarsigner"));
collectStoreParams(selfSigning, signCommandLine);
addParameter(signCommandLine, jar2Sign);
addParameter(signCommandLine, getAlias(selfSigning));
final int signedResult = startProcess(signCommandLine);
if (signedResult != 0) {
registerJavaFxPackagerError("JavaFX sign task has failed for: " + jar2Sign + ".");
}
}
private int genKey(String binPath) {
final String keyStorePath = getKeystore(true);
final File keyStoreFile = new File(keyStorePath);
if (keyStoreFile.isFile()) {
FileUtil.delete(keyStoreFile);
}
final List<String> genCommandLine = new ArrayList<String>();
addParameter(genCommandLine, FileUtil.toSystemDependentName(binPath + File.separator + "keytool"));
addParameter(genCommandLine, "-genkeypair");
addParameter(genCommandLine, "-dname");
String vendor = getVendor();
if (StringUtil.isEmptyOrSpaces(vendor)) {
vendor = "jb-fx-build";
}
addParameter(genCommandLine, "CN=" + vendor.replaceAll(",", "\\\\,"));
addParameter(genCommandLine, "-alias");
addParameter(genCommandLine, getAlias(true));
collectStoreParams(true, genCommandLine);
return startProcess(genCommandLine);
}
private void collectStoreParams(boolean selfSigning, List<String> signCommandLine) {
addParameter(signCommandLine, "-keyStore");
addParameter(signCommandLine, getKeystore(selfSigning));
addParameter(signCommandLine, "-storepass");
addParameter(signCommandLine, getStorepass(selfSigning));
addParameter(signCommandLine, "-keypass");
addParameter(signCommandLine, getKeypass(selfSigning));
}
private void copyResultsToArtifactsOutput(final File tempDirectory) {
try {
final File resultedJar = new File(getArtifactOutputPath());
FileUtil.copyDir(tempDirectory, resultedJar);
}
catch (IOException e) {
LOG.info(e);
}
FileUtil.delete(tempDirectory);
}
private void registerJavaFxPackagerError(Exception ex) {
registerJavaFxPackagerError(ex.getMessage());
}
private static void addParameter(List<String> commandLine, String param) {
if (!StringUtil.isEmptyOrSpaces(param)) {
commandLine.add(param);
}
}
private int startProcess(List<String> commands) {
try {
final Process process = new ProcessBuilder(CommandLineUtil.toCommandLine(commands)).start();
final String message = new String(FileUtil.loadBytes(process.getErrorStream()));
if (!StringUtil.isEmptyOrSpaces(message)) {
registerJavaFxPackagerError(message);
}
final int result = process.waitFor();
if (result != 0) {
final String explanationMessage = new String(FileUtil.loadBytes(process.getInputStream()));
if (!StringUtil.isEmptyOrSpaces(explanationMessage)) {
registerJavaFxPackagerError(explanationMessage);
}
}
return result;
}
catch (Exception e) {
registerJavaFxPackagerError(e);
return -1;
}
}
private int startAntTarget(String buildText, String javaHome) {
final String antHome = getAntHome();
if (antHome == null) {
registerJavaFxPackagerError("Bundled ant not found.");
return -1;
}
final ArrayList<String> commands = new ArrayList<String>();
commands.add(javaHome + File.separator + "bin" + File.separator + "java");
commands.add("-Dant.home=" + antHome);
commands.add("-classpath");
commands.add(antHome + "/lib/ant.jar" + File.pathSeparator +
antHome + "/lib/ant-launcher.jar" + File.pathSeparator +
javaHome + "/lib/ant-javafx.jar" + File.pathSeparator +
javaHome + "/jre/lib/jfxrt.jar");
commands.add("org.apache.tools.ant.launch.Launcher");
commands.add("-f");
try {
File tempFile = FileUtil.createTempFile("build", ".xml");
tempFile.deleteOnExit();
OutputStream outputStream = new FileOutputStream(tempFile.getAbsolutePath());
try {
outputStream.write(buildText.getBytes(Charset.defaultCharset()));
}
finally {
outputStream.close();
}
commands.add(tempFile.getCanonicalPath());
}
catch (IOException e) {
registerJavaFxPackagerError(e);
return -1;
}
return startProcess(commands);
}
private static String getAntHome() {
final String appHome = PathManager.getHomePath();
if (appHome == null) {
return null;
}
File antHome = new File(appHome, "lib" + File.separator + "ant");
if (!antHome.exists()) {
File communityAntHome = new File(appHome, "community" + File.separator + "lib" + File.separator + "ant");
if (communityAntHome.exists()) {
antHome = communityAntHome;
}
}
if (!antHome.exists()) {
return null;
}
return antHome.getPath();
}
private String getAlias(boolean selfSigning) {
return selfSigning ? "jb" : getAlias();
}
private String getKeypass(boolean selfSigning) {
return selfSigning ? "keypass" : Base64Converter.decode(getKeypass());
}
private String getKeystore(boolean selfSigning) {
return selfSigning ? getArtifactOutputPath() + File.separator + JB_JFX_JKS : getKeystore();
}
private String getStorepass(boolean selfSigning) {
return selfSigning ? "storepass" : Base64Converter.decode(getStorepass());
}
public abstract String getKeypass();
public abstract String getStorepass();
public abstract String getKeystore();
public abstract String getAlias();
public abstract boolean isSelfSigning();
public abstract boolean isEnabledSigning();
public abstract String getPreloaderClass();
public abstract String getPreloaderJar();
public abstract boolean convertCss2Bin();
public abstract List<JavaFxManifestAttribute> getCustomManifestAttributes();
}