| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.netbeans.nbbuild; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| import java.io.PrintWriter; |
| import java.io.Reader; |
| import java.io.StringWriter; |
| import java.io.Writer; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.net.URLDecoder; |
| import java.nio.charset.Charset; |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| import java.util.jar.JarEntry; |
| import java.util.jar.JarFile; |
| import java.util.jar.Manifest; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import java.util.zip.ZipEntry; |
| import javax.xml.parsers.ParserConfigurationException; |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.DirectoryScanner; |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.Task; |
| import org.apache.tools.ant.taskdefs.Copy; |
| import org.apache.tools.ant.taskdefs.Delete; |
| import org.apache.tools.ant.taskdefs.ManifestException; |
| import org.apache.tools.ant.taskdefs.SignJar; |
| import org.apache.tools.ant.taskdefs.Zip; |
| import org.apache.tools.ant.types.FileSet; |
| import org.apache.tools.ant.types.Resource; |
| import org.apache.tools.ant.types.ResourceCollection; |
| import org.apache.tools.ant.types.ZipFileSet; |
| import org.apache.tools.ant.types.resources.FileResource; |
| import org.apache.tools.ant.util.FileUtils; |
| import org.apache.tools.zip.ZipOutputStream; |
| import org.xml.sax.SAXException; |
| |
| /** Generates JNLP files for signed versions of the module JAR files. |
| * |
| * @author Jaroslav Tulach, Jesse Glick |
| */ |
| public class MakeJNLP extends Task { |
| /** the files to work on */ |
| private ResourceCollection files; |
| private SignJar signTask; |
| |
| private static final String MANIFEST = "META-INF/MANIFEST.MF"; //NOI18N |
| private static final String UTF_8 = "UTF-8"; //NOI18N |
| private static final String ATTR_CODEBASE = "Codebase"; //NOI18N |
| private static final String ATTR_PERMISSIONS = "Permissions"; //NOI18N |
| private static final String ATTR_APPLICATION_NAME = "Application-Name"; //NOI18N |
| |
| public FileSet createModules() |
| throws BuildException { |
| FileSet fs = new FileSet(); |
| fs.setProject(getProject()); |
| addConfigured(fs); |
| return fs; |
| } |
| |
| public void addConfigured(ResourceCollection rc) throws BuildException { |
| if (files != null) throw new BuildException("modules can be specified just once"); |
| files = rc; |
| } |
| |
| private SignJar getSignTask() { |
| if (signTask == null) { |
| signTask = (SignJar)getProject().createTask("signjar"); |
| } |
| return signTask; |
| } |
| |
| private File targetFile; |
| public void setDir(File t) { |
| targetFile = t; |
| } |
| |
| public void setAlias(String a) { |
| getSignTask().setAlias(a); |
| } |
| |
| public void setStorePass(String p) { |
| getSignTask().setStorepass(p); |
| } |
| |
| public void setKeystore(String k) { |
| getSignTask().setKeystore(k); |
| } |
| |
| public void setStoreType(String t) { |
| getSignTask().setStoretype(t); |
| } |
| |
| private String appName; |
| |
| public void setAppName(String appName) { |
| this.appName = appName; |
| } |
| |
| private final String manifestPermissions = "all-permissions"; |
| |
| private final String manifestCodebase = "*"; |
| |
| private String codebase = "$$codebase"; |
| public void setCodebase(String s) { |
| this.codebase = s; |
| } |
| |
| private boolean verify; |
| public void setVerify(boolean v) { |
| this.verify = v; |
| } |
| |
| private String verifyExcludes; |
| /** Comma separated list of allowed excluded names of files during verify |
| * phase. |
| */ |
| public void setVerifyExcludes(String s) { |
| this.verifyExcludes = s; |
| } |
| |
| private String permissions = "<all-permissions/>"; |
| /** |
| * XML fragment pasted into the security part of the .jnlp file. |
| * Should default to "<all-permissions/>" |
| */ |
| public void setPermissions(String s) { |
| permissions = s; |
| } |
| |
| private FileSet indirectJars; |
| /** |
| * Other JARs which should be copied into the destination directory and referred to as resources, |
| * even though they are not listed as Class-Path extensions of the module and would not normally |
| * be in its effective classpath. The basedir of the fileset should be a cluster root; for each |
| * such JAR, a file META-INF/clusterpath/$relpath will be inserted in the JAR, where $relpath is the |
| * relative path within the cluster. This permits the JAR to be located at runtime in a flat classpath, |
| * using ClassLoader.getResource. |
| */ |
| public void addIndirectJars(FileSet fs) { |
| indirectJars = fs; |
| } |
| |
| private FileSet indirectFiles; |
| /** |
| * Other non-JAR files which should be made available to InstalledFileLocator. |
| * The basedir of the fileset should be a cluster root; each |
| * such file will be packed into a ZIP entry META-INF/files/$relpath |
| * where the JAR will be available at runtime in a flat classpath, |
| * using ClassLoader.getResource. |
| */ |
| public void addIndirectFiles(FileSet fs) { |
| indirectFiles = fs; |
| } |
| |
| private boolean signJars = true; |
| /** |
| * Whether the final jars should be signed or not. Defaults to true |
| * (if not supplied). |
| */ |
| public void setSignJars(boolean s) { |
| this.signJars = s; |
| } |
| |
| private boolean processJarVersions = false; |
| /** |
| * Whether to add versions and sizes of jars into jnlp files. Defaults to false |
| * (if not supplied). |
| * @param b |
| */ |
| public void setProcessJarVersions(boolean b) { |
| this.processJarVersions = b; |
| } |
| |
| private Map<String, String> jarVersions; |
| /** |
| * Explicit definition of jar file versions (for jars without versions specified |
| * in manifest file) |
| * @param jarVersions |
| */ |
| public void setJarVersions(Map<String, String> jarVersions) { |
| this.jarVersions = jarVersions; |
| } |
| |
| private Set<String> nativeLibraries; |
| public void setNativeLibraries(Set<String> libs) { |
| this.nativeLibraries = libs; |
| } |
| |
| private Set<File> jarDirectories; |
| |
| private String includelocales; |
| public void setIncludelocales(String includelocales) { |
| this.includelocales = includelocales; |
| } |
| |
| /** |
| * Signs or copies the given files according to the signJars variable value. |
| */ |
| private void signOrCopy(File from, File to) { |
| if (!from.exists() && from.getParentFile().getName().equals("locale")) { |
| // skip missing locale files, probably the best fix for #103301 |
| log("Localization file " + from + " is referenced, but cannot be found. Skipping.", Project.MSG_WARN); |
| return; |
| } |
| if (signJars) { |
| getSignTask().setJar(from); |
| if (to != null) { |
| // #125970: might be .../modules/locale/something_ja.jar |
| to.getParentFile().mkdirs(); |
| } |
| getSignTask().setSignedjar(to); |
| getSignTask().setDigestAlg("SHA1"); |
| getSignTask().execute(); |
| } else if (to != null) { |
| Copy copy = (Copy)getProject().createTask("copy"); |
| copy.setFile(from); |
| copy.setTofile(to); |
| copy.execute(); |
| } |
| if (processJarVersions) { |
| if (jarDirectories == null) { |
| jarDirectories = new HashSet<>(); |
| } |
| jarDirectories.add(new File(to.getParent())); |
| } |
| } |
| |
| @Override |
| public void execute() throws BuildException { |
| if (targetFile == null) throw new BuildException("Output dir must be provided"); |
| if (files == null) throw new BuildException("modules must be provided"); |
| try { |
| generateFiles(); |
| if (processJarVersions && jarDirectories!=null && jarDirectories.size() > 0) { |
| generateVersionXMLFiles(); |
| } |
| } catch (IOException ex) { |
| throw new BuildException(ex); |
| } |
| } |
| |
| private void generateFiles() throws IOException, BuildException { |
| Set<String> declaredLocales = new HashSet<>(); |
| boolean useAllLocales = false; |
| if(includelocales == null || "*".equals(includelocales)) { |
| useAllLocales = true; |
| } else if ("".equals(includelocales)) { |
| useAllLocales = false; |
| } else { |
| StringTokenizer tokenizer = new StringTokenizer(includelocales, ","); |
| while (tokenizer.hasMoreElements()) { |
| declaredLocales.add(tokenizer.nextToken()); |
| } |
| } |
| Set<String> indirectFilePaths = new HashSet<>(); |
| File tmpFile = null; |
| for (FileSet fs : new FileSet[] {indirectJars, indirectFiles}) { |
| if (fs != null) { |
| DirectoryScanner scan = fs.getDirectoryScanner(getProject()); |
| for (String f : scan.getIncludedFiles()) { |
| indirectFilePaths.add(f.replace(File.separatorChar, '/')); |
| } |
| } |
| } |
| |
| for (Iterator<Resource> fileIt = files.iterator(); fileIt.hasNext();) { |
| FileResource fr = (FileResource) fileIt.next(); |
| File jar = fr.getFile(); |
| |
| if (!jar.canRead()) { |
| throw new BuildException("Cannot read file: " + jar); |
| } |
| |
| try (JarFile theJar = new JarFile(jar)) { |
| String codenamebase = JarWithModuleAttributes.extractCodeName(theJar.getManifest().getMainAttributes()); |
| if (codenamebase == null) { |
| throw new BuildException("Not a NetBeans Module: " + jar); |
| } |
| if ( |
| codenamebase.equals("org.objectweb.asm.all") && |
| jar.getParentFile().getName().equals("core") && |
| jar.getParentFile().getParentFile().getName().startsWith("platform") |
| ) { |
| continue; |
| } |
| { |
| int slash = codenamebase.indexOf('/'); |
| if (slash >= 0) { |
| codenamebase = codenamebase.substring(0, slash); |
| } |
| } |
| |
| String dashcnb = codenamebase.replace('.', '-'); |
| String title; |
| String oneline; |
| String shrt; |
| String osDep = null; |
| |
| { |
| String bundle = theJar.getManifest().getMainAttributes().getValue("OpenIDE-Module-Localizing-Bundle"); |
| Properties prop = new Properties(); |
| if (bundle != null) { |
| ZipEntry en = theJar.getEntry(bundle); |
| if (en == null) { |
| throw new BuildException("Cannot find entry: " + bundle + " in file: " + jar); |
| } try (InputStream is = theJar.getInputStream(en)) { |
| prop.load(is); |
| } |
| } |
| title = prop.getProperty("OpenIDE-Module-Name", codenamebase); |
| oneline = prop.getProperty("OpenIDE-Module-Short-Description", title); |
| shrt = prop.getProperty("OpenIDE-Module-Long-Description", oneline); |
| } |
| { |
| String osMan = theJar.getManifest().getMainAttributes().getValue("OpenIDE-Module-Requires"); |
| if (osMan != null) { |
| if (osMan.indexOf("org.openide.modules.os.MacOSX") >= 0) { // NOI18N |
| osDep = "Mac OS X"; // NOI18N |
| } else if (osMan.indexOf("org.openide.modules.os.Linux") >= 0) { // NOI18N |
| osDep = "Linux"; // NOI18N |
| } else if (osMan.indexOf("org.openide.modules.os.Solaris") >= 0) { // NOI18N |
| osDep = "Solaris"; // NOI18N |
| } else if (osMan.indexOf("org.openide.modules.os.Windows") >= 0) { // NOI18N |
| osDep = "Windows"; // NOI18N |
| } |
| } |
| } Map<String,List<File>> localizedFiles = verifyExtensions(jar, theJar.getManifest(), dashcnb, codenamebase, verify, indirectFilePaths); |
| new File(targetFile, dashcnb).mkdir(); |
| File signed = new File(new File(targetFile, dashcnb), jar.getName()); |
| File jnlp = new File(targetFile, dashcnb + ".jnlp"); |
| StringWriter writeJNLP = new StringWriter(); |
| writeJNLP.write("<?xml version='1.0' encoding='UTF-8'?>\n"); |
| writeJNLP.write("<!DOCTYPE jnlp PUBLIC \"-//Sun Microsystems, Inc//DTD JNLP Descriptor 6.0//EN\" \"http://java.sun.com/dtd/JNLP-6.0.dtd\">\n"); |
| writeJNLP.write("<jnlp spec='1.0+' codebase='" + codebase + "'>\n"); |
| writeJNLP.write(" <information>\n"); |
| writeJNLP.write(" <title>" + XMLUtil.toElementContent(title) + "</title>\n"); |
| writeJNLP.write(" <vendor>NetBeans</vendor>\n"); |
| writeJNLP.write(" <description kind='one-line'>" + XMLUtil.toElementContent(oneline) + "</description>\n"); |
| writeJNLP.write(" <description kind='short'>" + XMLUtil.toElementContent(shrt) + "</description>\n"); |
| writeJNLP.write(" </information>\n"); |
| writeJNLP.write(permissions +"\n"); |
| if (osDep == null) { |
| writeJNLP.write(" <resources>\n"); |
| } else { |
| writeJNLP.write(" <resources os='" + osDep + "'>\n"); |
| } writeJNLP.write(constructJarHref(jar, dashcnb)); |
| processExtensions(jar, theJar.getManifest(), writeJNLP, dashcnb, codebase); |
| processIndirectJars(writeJNLP, dashcnb); |
| processIndirectFiles(writeJNLP, dashcnb); |
| writeJNLP.write(" </resources>\n"); |
| if (useAllLocales || !declaredLocales.isEmpty()){ |
| // write down locales |
| for (Map.Entry<String,List<File>> e : localizedFiles.entrySet()) { |
| String locale = e.getKey(); |
| if (!declaredLocales.isEmpty() && !declaredLocales.contains(locale)) { |
| continue; |
| } |
| List<File> allFiles = e.getValue(); |
| |
| writeJNLP.write(" <resources locale='" + locale + "'>\n"); |
| |
| for (File n : allFiles) { |
| log("generating locale " + locale + " for " + n, Project.MSG_VERBOSE); |
| String name = n.getName(); |
| String clusterRootPrefix = jar.getParent() + File.separatorChar; |
| String absname = n.getAbsolutePath(); |
| if (absname.startsWith(clusterRootPrefix)) { |
| name = absname.substring(clusterRootPrefix.length()).replace(File.separatorChar, '-'); |
| } |
| File t = new File(new File(targetFile, dashcnb), name); |
| |
| File localeTmpFile = null; |
| if (n.exists() && isSigned(n) == null) { |
| try { |
| localeTmpFile = extendLibraryManifest(getProject(), n, t, manifestCodebase, manifestPermissions, appName, jnlp); |
| } catch (IOException | ManifestException ex) { |
| getProject().log( |
| "Failed to extend libraries manifests: " + ex.getMessage(), //NOI18N |
| Project.MSG_WARN); |
| } |
| } else { |
| getProject().log( |
| String.format( |
| "Not adding security attributes into locale library: %s the library is already signed.", |
| safeRelativePath(getProject().getBaseDir(),t)), |
| Project.MSG_WARN); |
| } |
| if (localeTmpFile != null) { |
| signOrCopy(localeTmpFile, t); |
| deleteTmpFile(localeTmpFile); |
| } |
| else { |
| signOrCopy(n, t); |
| } |
| writeJNLP.write(constructJarHref(n, dashcnb, name)); |
| } |
| |
| writeJNLP.write(" </resources>\n"); |
| |
| } |
| } writeJNLP.write(" <component-desc/>\n"); |
| writeJNLP.write("</jnlp>\n"); |
| writeJNLP.close(); |
| FileWriter w = new FileWriter(jnlp); |
| w.write(writeJNLP.toString()); |
| w.close(); |
| if (jar.exists() && isSigned(jar) == null) { |
| try { |
| tmpFile = extendLibraryManifest(getProject(), jar, signed, manifestCodebase, manifestPermissions, appName, jnlp); |
| } catch (IOException | ManifestException ex) { |
| getProject().log( |
| "Failed to extend libraries manifests: " + ex.getMessage(), //NOI18N |
| Project.MSG_WARN); |
| } |
| } else { |
| getProject().log( |
| String.format( |
| "Not adding security attributes into library: %s the library is already signed.", |
| safeRelativePath(getProject().getBaseDir(),signed)), |
| Project.MSG_WARN); |
| } if (tmpFile != null) { |
| signOrCopy(tmpFile, signed); |
| deleteTmpFile(tmpFile); |
| } else { |
| signOrCopy(jar, signed); |
| } |
| } |
| } |
| |
| } |
| |
| private File extendLibraryManifest( |
| final Project prj, |
| final File sourceJar, |
| final File signedJar, |
| final String codebase, |
| final String permissions, |
| final String appName, |
| final File jnlp) throws IOException, ManifestException { |
| org.apache.tools.ant.taskdefs.Manifest manifest = null; |
| Copy cp = new Copy(); |
| File tmpFile = new File(String.format("%s.tmp", signedJar.getAbsolutePath())); |
| cp.setFile(sourceJar); |
| cp.setTofile(tmpFile); |
| cp.execute(); |
| boolean success = false; |
| try { |
| final Map<String,String> extendedAttrs = new HashMap<>(); |
| final org.apache.tools.zip.ZipFile zf = new org.apache.tools.zip.ZipFile(sourceJar); |
| try { |
| final org.apache.tools.zip.ZipEntry manifestEntry = zf.getEntry(MANIFEST); |
| if (manifestEntry != null) { |
| final Reader in = new InputStreamReader(zf.getInputStream(manifestEntry), Charset.forName(UTF_8)); //NOI18N |
| try { |
| manifest = new org.apache.tools.ant.taskdefs.Manifest(in); |
| } finally { |
| in.close(); |
| } |
| } else { |
| manifest = new org.apache.tools.ant.taskdefs.Manifest(); |
| } |
| final org.apache.tools.ant.taskdefs.Manifest.Section mainSection = manifest.getMainSection(); |
| String attr = mainSection.getAttributeValue(ATTR_CODEBASE); |
| if (attr == null) { |
| mainSection.addAttributeAndCheck(new org.apache.tools.ant.taskdefs.Manifest.Attribute( |
| ATTR_CODEBASE, |
| codebase)); |
| extendedAttrs.put(ATTR_CODEBASE, codebase); |
| } |
| attr = mainSection.getAttributeValue(ATTR_PERMISSIONS); |
| if (attr == null) { |
| mainSection.addAttributeAndCheck(new org.apache.tools.ant.taskdefs.Manifest.Attribute( |
| ATTR_PERMISSIONS, |
| permissions)); |
| extendedAttrs.put(ATTR_PERMISSIONS, permissions); |
| } |
| attr = mainSection.getAttributeValue(ATTR_APPLICATION_NAME); |
| if (attr == null) { |
| mainSection.addAttributeAndCheck(new org.apache.tools.ant.taskdefs.Manifest.Attribute( |
| ATTR_APPLICATION_NAME, |
| appName)); |
| extendedAttrs.put(ATTR_APPLICATION_NAME, appName); |
| } |
| if (!extendedAttrs.isEmpty()) { |
| final Enumeration<? extends org.apache.tools.zip.ZipEntry> zent = zf.getEntries(); |
| final ZipOutputStream out = new ZipOutputStream(tmpFile); |
| try { |
| if (jnlp != null) { |
| org.apache.tools.zip.ZipEntry jnlpEntry = new org.apache.tools.zip.ZipEntry("JNLP-INF/" + jnlp.getName()); |
| out.putNextEntry(jnlpEntry); |
| FileInputStream fis = new FileInputStream(jnlp.getAbsolutePath()); |
| try { |
| copy(fis, out); |
| } finally { |
| fis.close(); |
| } |
| } |
| while (zent.hasMoreElements()) { |
| final org.apache.tools.zip.ZipEntry entry = zent.nextElement(); |
| final InputStream in = zf.getInputStream(entry); |
| try { |
| out.putNextEntry(entry); |
| if (MANIFEST.equals(entry.getName())) { |
| final PrintWriter manifestOut = new PrintWriter(new OutputStreamWriter(out, Charset.forName(UTF_8))); |
| manifest.write(manifestOut); |
| manifestOut.flush(); |
| } else { |
| copy(in,out); |
| } |
| } finally { |
| in.close(); |
| } |
| } |
| } finally { |
| out.close(); |
| } |
| success = true; |
| final StringBuilder message = new StringBuilder("Updating library "). //NOI18N |
| append(safeRelativePath(prj.getBaseDir(), tmpFile)). |
| append(" manifest"); //NOI18N |
| for (Map.Entry<String,String> e : extendedAttrs.entrySet()) { |
| message.append(String.format(" %s: %s,", e.getKey(), e.getValue())); |
| } |
| message.deleteCharAt(message.length()-1); |
| prj.log(message.toString(), Project.MSG_VERBOSE); |
| } |
| } finally { |
| zf.close(); |
| } |
| } finally { |
| if (!success) { |
| final Delete rm = new Delete(); |
| rm.setFile(tmpFile); |
| rm.setQuiet(true); |
| rm.execute(); |
| tmpFile = null; |
| } |
| } |
| return tmpFile; |
| } |
| |
| private static void deleteTmpFile(File tmpFile) { |
| final Delete del = new Delete(); |
| del.setFile(tmpFile); |
| del.execute(); |
| } |
| |
| private static void copy(final InputStream in, final OutputStream out) throws IOException { |
| final byte[] BUFFER = new byte[4096]; |
| int len; |
| for (;;) { |
| len = in.read(BUFFER); |
| if (len == -1) { |
| return; |
| } |
| out.write(BUFFER, 0, len); |
| } |
| } |
| |
| private static String safeRelativePath(File from, File to) { |
| try { |
| return FileUtils.getRelativePath(from, to); |
| } catch (Exception ex) { |
| return to.getAbsolutePath(); |
| } |
| } |
| |
| |
| private Map<String,List<File>> verifyExtensions(File f, Manifest mf, String dashcnb, String codebasename, boolean verify, Set<String> indirectFilePaths) throws IOException, BuildException { |
| Map<String,List<File>> localizedFiles = new HashMap<>(); |
| |
| |
| File clusterRoot = f.getParentFile(); |
| String moduleDirPrefix = ""; |
| File updateTracking; |
| log("Verifying extensions for: " + codebasename + ", cluster root: " + clusterRoot + ", verify: " + verify, Project.MSG_DEBUG); |
| for(;;) { |
| updateTracking = new File(clusterRoot, "update_tracking"); |
| if (updateTracking.isDirectory()) { |
| break; |
| } |
| moduleDirPrefix = clusterRoot.getName() + "/" + moduleDirPrefix; |
| clusterRoot = clusterRoot.getParentFile(); |
| if (clusterRoot == null || !clusterRoot.exists()) { |
| if (!verify) { |
| return localizedFiles; |
| } |
| |
| throw new BuildException("Cannot find update_tracking directory for module " + f); |
| } |
| } |
| |
| File ut = new File(updateTracking, dashcnb + ".xml"); |
| if (!ut.exists()) { |
| throw new BuildException("The file " + ut + " for module " + codebasename + " cannot be found"); |
| } |
| |
| Map<String,String> fileToOwningModule = new HashMap<>(); |
| try { |
| ModuleSelector.readUpdateTracking(getProject(), ut.toString(), fileToOwningModule); |
| } catch (IOException | ParserConfigurationException | SAXException ex) { |
| throw new BuildException(ex); |
| } |
| |
| log("project files: " + fileToOwningModule, Project.MSG_DEBUG); |
| String name = relative(f, clusterRoot); |
| log(" removing: " + name, Project.MSG_DEBUG); |
| removeWithLocales(fileToOwningModule, name, clusterRoot, localizedFiles); |
| name = "config/Modules/" + dashcnb + ".xml"; |
| log(" removing: " + name, Project.MSG_DEBUG); |
| removeWithLocales(fileToOwningModule, name, clusterRoot, localizedFiles); |
| name = "config/ModuleAutoDeps/" + dashcnb + ".xml"; |
| log(" removing: " + name, Project.MSG_DEBUG); |
| removeWithLocales(fileToOwningModule, name, clusterRoot, localizedFiles); |
| name = "update_tracking/" + dashcnb + ".xml"; |
| log(" removing: " + name, Project.MSG_DEBUG); |
| removeWithLocales(fileToOwningModule, name, clusterRoot, localizedFiles); |
| |
| |
| |
| |
| String path = mf.getMainAttributes().getValue("Class-Path"); |
| if (path != null) { |
| StringTokenizer tok = new StringTokenizer(path, ", "); |
| while(tok.hasMoreElements()) { |
| String s = tok.nextToken(); |
| File e = new File(f.getParentFile(), s); |
| String r = relative(e, clusterRoot); |
| removeWithLocales(fileToOwningModule, r, clusterRoot, localizedFiles); |
| } |
| } |
| |
| fileToOwningModule.remove("ant/nblib/" + dashcnb + ".jar"); |
| |
| fileToOwningModule.remove("VERSION.txt"); // cluster release information |
| |
| log(" removing: " + indirectFilePaths, Project.MSG_DEBUG); |
| fileToOwningModule.keySet().removeAll(indirectFilePaths); |
| |
| if (verifyExcludes != null) { |
| StringTokenizer tok = new StringTokenizer(verifyExcludes, ", "); |
| while(tok.hasMoreElements()) { |
| removeWithLocales(fileToOwningModule, tok.nextToken(), clusterRoot, localizedFiles); |
| } |
| } |
| |
| |
| if (verify) { |
| if (!fileToOwningModule.isEmpty()) { |
| throw new BuildException( |
| "Cannot build JNLP for module " + f + " as these files are in " + |
| "module's NBM, but are not referenced from any path (see harness/README for properties you can define to fix):\n" + fileToOwningModule.keySet() |
| ); |
| } |
| } |
| |
| return localizedFiles; |
| } |
| |
| private static void removeWithLocales(Map<String,String> removeFrom, String removeWhat, File clusterRoot, Map<String,List<File>> recordLocales) { |
| if (removeFrom.remove(removeWhat) != null && removeWhat.endsWith(".jar")) { |
| int basedir = removeWhat.lastIndexOf('/'); |
| String base = basedir == -1 ? "" : removeWhat.substring(0, basedir); |
| String name = removeWhat.substring(basedir + 1, removeWhat.length() - 4); |
| Pattern p = Pattern.compile(base + "/locale/" + name + "(|_[a-zA-Z0-9_]+)\\.jar"); |
| |
| Iterator<String> it = removeFrom.keySet().iterator(); |
| while (it.hasNext()) { |
| String s = it.next(); |
| Matcher m = p.matcher(s); |
| if (m.matches()) { |
| String locale = m.group(1).substring(1); |
| |
| List<File> l = recordLocales.get(locale); |
| if (l == null) { |
| l = new ArrayList<>(); |
| recordLocales.put(locale, l); |
| } |
| l.add(new File(clusterRoot, s.replace('/', File.separatorChar))); |
| it.remove(); |
| } |
| } |
| } |
| } |
| |
| private void processExtensions(File f, Manifest mf, Writer fileWriter, String dashcnb, String codebase) throws IOException, BuildException { |
| |
| File nblibJar = new File(new File(new File(f.getParentFile().getParentFile(), "ant"), "nblib"), dashcnb + ".jar"); |
| if (nblibJar.isFile()) { |
| File ext = new File(new File(targetFile, dashcnb), "ant-nblib-" + nblibJar.getName()); |
| fileWriter.write(constructJarHref(ext, dashcnb)); |
| signOrCopy(nblibJar, ext); |
| } |
| |
| String path = mf.getMainAttributes().getValue("Class-Path"); |
| if (path == null) { |
| return; |
| } |
| |
| StringTokenizer tok = new StringTokenizer(path, ", "); |
| while(tok.hasMoreElements()) { |
| String s = URLDecoder.decode(tok.nextToken(), "UTF-8"); |
| |
| if (s.contains("${java.home}")) { |
| continue; |
| } |
| |
| File e = new File(f.getParentFile(), s); |
| if (!e.canRead()) { |
| throw new BuildException("Cannot read extension " + e + " referenced from " + f); |
| } |
| String n = e.getName(); |
| if (n.endsWith(".jar")) { |
| n = n.substring(0, n.length() - 4); |
| } |
| File ext = new File(new File(targetFile, dashcnb), s.replace("../", "").replace('/', '-')); |
| |
| if (e.exists() && isSigned(e) != null) { |
| getProject().log( |
| String.format( |
| "Not adding security attributes into library: %s the library is already signed.", |
| safeRelativePath(getProject().getBaseDir(),ext)), |
| Project.MSG_WARN); |
| Copy copy = (Copy)getProject().createTask("copy"); |
| copy.setFile(e); |
| copy.setTofile(ext); |
| copy.execute(); |
| |
| String extJnlpName = dashcnb + '-' + ext.getName().replaceFirst("\\.jar$", "") + ".jnlp"; |
| File jnlp = new File(targetFile, extJnlpName); |
| |
| try (FileWriter writeJNLP = new FileWriter(jnlp)) { |
| writeJNLP.write("<?xml version='1.0' encoding='UTF-8'?>\n"); |
| writeJNLP.write("<!DOCTYPE jnlp PUBLIC \"-//Sun Microsystems, Inc//DTD JNLP Descriptor 6.0//EN\" \"http://java.sun.com/dtd/JNLP-6.0.dtd\">\n"); |
| writeJNLP.write("<jnlp spec='1.0+' codebase='" + codebase + "'>\n"); |
| writeJNLP.write(" <information>\n"); |
| writeJNLP.write(" <title>" + n + "</title>\n"); |
| writeJNLP.write(" <vendor>NetBeans</vendor>\n"); |
| writeJNLP.write(" </information>\n"); |
| writeJNLP.write(permissions +"\n"); |
| writeJNLP.write(" <resources>\n"); |
| writeJNLP.write(constructJarHref(ext, dashcnb)); |
| writeJNLP.write(" </resources>\n"); |
| writeJNLP.write(" <component-desc/>\n"); |
| writeJNLP.write("</jnlp>\n"); |
| } |
| |
| fileWriter.write(" <extension name='" + e.getName().replaceFirst("\\.jar$", "") + "' href='" + extJnlpName + "'/>\n"); |
| } else { |
| File tmpFile = null; |
| try { |
| tmpFile = extendLibraryManifest(getProject(), e, ext, manifestCodebase, manifestPermissions, appName, null); |
| } catch (IOException | ManifestException ex) { |
| getProject().log( |
| "Failed to extend libraries manifests: " + ex.getMessage(), //NOI18N |
| Project.MSG_WARN); |
| } |
| if (tmpFile != null) { |
| signOrCopy(tmpFile, ext); |
| deleteTmpFile(tmpFile); |
| } else { |
| signOrCopy(e, ext); |
| } |
| |
| fileWriter.write(constructJarHref(ext, dashcnb)); |
| } |
| } |
| } |
| |
| private void processIndirectJars(Writer fileWriter, String dashcnb) throws IOException, BuildException { |
| if (indirectJars == null) { |
| return; |
| } |
| DirectoryScanner scan = indirectJars.getDirectoryScanner(getProject()); |
| for (String f : scan.getIncludedFiles()) { |
| File jar = new File(scan.getBasedir(), f); |
| String rel = f.replace(File.separatorChar, '/'); |
| String sig; |
| try { |
| sig = isSigned(jar); |
| } catch (IOException x) { |
| throw new BuildException("Cannot check signature on " + jar, x, getLocation()); |
| } |
| // javaws will reject .zip files even with signatures. |
| String rel2 = rel.endsWith(".jar") ? rel : rel.replaceFirst("(\\.zip)?$", ".jar"); |
| File ext = new File(new File(targetFile, dashcnb), rel2.replace('/', '-').replaceFirst("^modules-", "")); |
| Zip jartask = (Zip) getProject().createTask("jar"); |
| jartask.setDestFile(ext); |
| ZipFileSet zfs = new ZipFileSet(); |
| zfs.setSrc(jar); |
| if (sig != null) { |
| // Need to cancel original signature since we are adding one entry to the JAR. |
| zfs.setExcludes("META-INF/" + sig + ".*"); |
| } |
| jartask.addZipfileset(zfs); |
| zfs = new ZipFileSet(); |
| File blank = File.createTempFile("empty", ""); |
| blank.deleteOnExit(); |
| zfs.setFile(blank); |
| zfs.setFullpath("META-INF/clusterpath/" + rel); |
| jartask.addZipfileset(zfs); |
| jartask.execute(); |
| blank.delete(); |
| |
| fileWriter.write(constructJarHref(ext, dashcnb)); |
| signOrCopy(ext, null); |
| } |
| } |
| |
| private void processIndirectFiles(Writer fileWriter, String dashcnb) throws IOException, BuildException { |
| if (indirectFiles == null) { |
| return; |
| } |
| DirectoryScanner scan = indirectFiles.getDirectoryScanner(getProject()); |
| Map<String,File> entries = new LinkedHashMap<>(); |
| for (String f : scan.getIncludedFiles()) { |
| entries.put(f.replace(File.separatorChar, '/'), new File(scan.getBasedir(), f)); |
| } |
| if (entries.isEmpty()) { |
| return; |
| } |
| File ext = new File(new File(targetFile, dashcnb), "extra-files.jar"); |
| Zip jartask = (Zip) getProject().createTask("jar"); |
| jartask.setDestFile(ext); |
| for (Map.Entry<String,File> entry : entries.entrySet()) { |
| ZipFileSet zfs = new ZipFileSet(); |
| zfs.setFile(entry.getValue()); |
| zfs.setFullpath("META-INF/files/" + entry.getKey()); |
| jartask.addZipfileset(zfs); |
| } |
| jartask.execute(); |
| fileWriter.write(constructJarHref(ext, dashcnb)); |
| signOrCopy(ext, null); |
| } |
| |
| private String relative(File file, File root) { |
| String sfile = file.toString().replace(File.separatorChar, '/'); |
| String sroot = (root.toString() + File.separator).replace(File.separatorChar, '/'); |
| if (sfile.startsWith(sroot)) { |
| try { |
| String result = new URI(null, sfile.substring(sroot.length()), null).normalize().getPath(); |
| return result; |
| } catch (URISyntaxException x) { |
| throw new BuildException(x, getLocation()); // or just ignore? |
| } |
| } |
| return sfile; |
| } |
| |
| /** return alias if signed, or null if not */ |
| private static String isSigned(File f) throws IOException { |
| try (JarFile jar = new JarFile(f)) { |
| Enumeration<JarEntry> en = jar.entries(); |
| while (en.hasMoreElements()) { |
| Matcher m = SF.matcher(en.nextElement().getName()); |
| if (m.matches()) { |
| return m.group(1); |
| } |
| } |
| return null; |
| } |
| } |
| private static final Pattern SF = Pattern.compile("META-INF/(.+)\\.SF"); |
| |
| /** |
| * returns version of jar file depending on manifest.mf or explicitly specified |
| * @param jar |
| * @return |
| * @throws IOException |
| */ |
| private String getJarVersion(JarFile jar) throws IOException { |
| String version = jar.getManifest().getMainAttributes().getValue("OpenIDE-Module-Specification-Version"); |
| if (version == null) { |
| version = jar.getManifest().getMainAttributes().getValue("Specification-Version"); |
| } |
| if (version == null && jarVersions != null) { |
| version = jarVersions.get(jar.getName()); |
| } |
| return version; |
| } |
| |
| /** |
| * Constructs jar or nativelib tag for jars |
| * @param f |
| * @param dashcnb |
| * @return |
| * @throws IOException |
| */ |
| private String constructJarHref(File f, String dashcnb) throws IOException { |
| return constructJarHref(f, dashcnb, f.getName()); |
| } |
| |
| /** |
| * Constructs jar or nativelib tag for jars using custom name |
| * @param f |
| * @param dashcnb |
| * @return |
| * @throws IOException |
| */ |
| private String constructJarHref(File f, String dashcnb, String name) throws IOException { |
| String tag = "jar"; |
| if (nativeLibraries != null && nativeLibraries.contains(name)) { |
| tag = "nativelib"; |
| } |
| if (processJarVersions) { |
| if (!f.exists()) { |
| throw new BuildException("JAR file " + f + " does not exist, cannot extract required versioning info."); |
| } |
| JarFile extJar = new JarFile(f); |
| String version = getJarVersion(extJar); |
| if (version != null) { |
| return " <" + tag + " href='" + dashcnb + '/' + name + "' version='" + version + "' size='" + f.length() + "'/>\n"; |
| } |
| } |
| return " <" + tag + " href='" + dashcnb + '/' + name + "'/>\n"; |
| } |
| |
| private void generateVersionXMLFiles() throws IOException { |
| FileSet fs = new FileSet(); |
| fs.setIncludes("**/*.jar"); |
| for (File directory : jarDirectories) { |
| fs.setDir(directory); |
| DirectoryScanner scan = fs.getDirectoryScanner(getProject()); |
| StringWriter writeVersionXML = new StringWriter(); |
| writeVersionXML.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); |
| writeVersionXML.append("<jnlp-versions>\n"); |
| for (String jarName : scan.getIncludedFiles()) { |
| File jar = new File(scan.getBasedir(), jarName); |
| JarFile jarFile = new JarFile(jar); |
| String version = getJarVersion(jarFile); |
| if (version != null) { |
| writeVersionXML.append(" <resource>\n <pattern>\n <name>"); |
| writeVersionXML.append(jar.getName()); |
| writeVersionXML.append("</name>\n <version-id>"); |
| writeVersionXML.append(version); |
| writeVersionXML.append("</version-id>\n </pattern>\n <file>"); |
| writeVersionXML.append(jar.getName()); |
| writeVersionXML.append("</file>\n </resource>\n"); |
| } else { |
| writeVersionXML.append(" <!-- No version found for "); |
| writeVersionXML.append(jar.getName()); |
| writeVersionXML.append(" -->\n"); |
| } |
| } |
| writeVersionXML.append("</jnlp-versions>\n"); |
| writeVersionXML.close(); |
| |
| File versionXML = new File(directory, "version.xml"); |
| FileWriter w = new FileWriter(versionXML); |
| w.write(writeVersionXML.toString()); |
| w.close(); |
| } |
| } |
| |
| } |