/* * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jnlp.converter.parser; import java.net.URL; import java.util.Properties; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import jnlp.converter.HTTPHelper; /** * This class contains information about the codebase and properties, i.e., how * to locate the classes and optional-packages */ public class ResourcesDesc implements ResourceType { private final List _list; private volatile JNLPDesc _parent = null; /** * Create empty resource list */ public ResourcesDesc() { _list = new CopyOnWriteArrayList<>(); } public JNLPDesc getParent() { return _parent; } void setParent(JNLPDesc parent) { _parent = parent; for (int i = 0; i < _list.size(); i++) { Object o = _list.get(i); if (o instanceof JREDesc) { JREDesc jredesc = (JREDesc) o; if (jredesc.getNestedResources() != null) { jredesc.getNestedResources().setParent(parent); } } } } public void addResource(ResourceType rd) { if (rd != null) { _list.add(rd); } } boolean isEmpty() { return _list.isEmpty(); } public JARDesc[] getLocalJarDescs() { ArrayList jds = new ArrayList<>(_list.size()); for (ResourceType rt : _list) { if (rt instanceof JARDesc) { jds.add((JARDesc) rt); } } return jds.toArray(new JARDesc[jds.size()]); } public JREDesc getJreDesc() { for (ResourceType rt : _list) { if (rt instanceof JREDesc) { return (JREDesc)rt; } } return null; } public ExtensionDesc[] getExtensionDescs() throws Exception { final ArrayList extList = new ArrayList<>(); visit(new ResourceVisitor() { @Override public void visitExtensionDesc(ExtensionDesc ed) throws Exception { // add all extensiondesc recursively addExtToList(extList); } }); return extList.toArray(new ExtensionDesc[extList.size()]); } public JARDesc[] getAllJarDescs() throws Exception { List jarList = new ArrayList<>(); addJarsToList(jarList); return jarList.toArray(new JARDesc[jarList.size()]); } /** * Add to a list of all the ExtensionDesc. This method goes recusivly through * all ExtensionDesc */ private void addExtToList(final List list) throws Exception { // Iterate through list an add ext jnlp to the list. visit(new ResourceVisitor() { @Override public void visitExtensionDesc(ExtensionDesc ed) throws Exception { if (ed.getExtensionDesc() != null) { ed.getExtensionDesc().getMainJar(); ResourcesDesc rd = ed.getExtensionDesc().getResourcesDesc(); if (rd != null) { rd.addExtToList(list); } } list.add(ed); } }); } private void addJarsToList(final List list) throws Exception { // Iterate through list an add resources to the list. // The ordering of resources are preserved visit(new ResourceVisitor() { @Override public void visitJARDesc(JARDesc jd) { list.add(jd); } @Override public void visitExtensionDesc(ExtensionDesc ed) throws Exception { if (ed.getExtensionDesc() != null) { ResourcesDesc rd = ed.getExtensionDesc().getResourcesDesc(); if (rd != null) { rd.addJarsToList(list); } } } }); } /** * Get all the resources needed when a specific resource is requested. * Returns null if no resource was found */ public JARDesc[] getResource(final URL location) throws Exception { final JARDesc[] resources = new JARDesc[1]; // Find the given resource visit(new ResourceVisitor() { @Override public void visitJARDesc(JARDesc jd) { if (GeneralUtil.sameURLs(jd.getLocation(), location)) { resources[0] = jd; } } }); // Found no resource? if (resources[0] == null) { return null; } // No part, so just one resource return resources; } /* Returns the Expected Main Jar * first jar with attribute main="true" * else first jar if none has that attribute * will look in extensions, and nested resource blocks if matching */ protected JARDesc getMainJar() throws Exception { // Normal trick to get around final arguments to inner classes final JARDesc[] results = new JARDesc[2]; visit(new ResourceVisitor() { @Override public void visitJARDesc(JARDesc jd) { if (jd.isJavaFile()) { // Keep track of first Java File if (results[0] == null || results[0].isNativeLib()) { results[0] = jd; } // Keep tack of Java File marked main if (jd.isMainJarFile()) { results[1] = jd; } } else if (jd.isNativeLib()) { // if jnlp extension has only native lib if (results[0] == null) { results[0] = jd; } } } @Override public void visitExtensionDesc(ExtensionDesc ed) throws Exception { // only check if no main yet and it is not an installer if (results[1] == null && !ed.isInstaller()) { JNLPDesc extLd = ed.getExtensionDesc(); if (extLd != null && extLd.isLibrary()) { ResourcesDesc rd = extLd.getResourcesDesc(); if (rd != null) { // look for main jar in extension resource rd.visit(this); } } } } }); // Default is the first, if none is specified as main. This might // return NULL if there is no JAR resources. JARDesc first = results[0]; JARDesc main = results[1]; // if main is null then return first; // libraries have no such thing as a main jar, so return first; // otherwise return main // only returns null if there are no jars. return (main == null) ? first : main; } /* * Get the properties defined for this object */ public Properties getResourceProperties() throws Exception { final Properties props = new Properties(); visit(new ResourceVisitor() { @Override public void visitPropertyDesc(PropertyDesc pd) { props.setProperty(pd.getKey(), pd.getValue()); } @Override public void visitExtensionDesc(ExtensionDesc ed) throws Exception { JNLPDesc jnlpd = ed.getExtensionDesc(); ResourcesDesc rd = jnlpd.getResourcesDesc(); if (rd != null) { Properties extProps = rd.getResourceProperties(); Enumeration e = extProps.propertyNames(); while (e.hasMoreElements()) { String key = (String) e.nextElement(); String value = extProps.getProperty(key); props.setProperty(key, value); } } } }); return props; } /* * Get the properties defined for this object, in the right order. */ public List getResourcePropertyList() throws Exception { final LinkedList propList = new LinkedList<>(); visit(new ResourceVisitor() { @Override public void visitPropertyDesc(PropertyDesc pd) { propList.add(new Property(pd.getKey(), pd.getValue())); } }); return propList; } /** * visitor dispatch */ @Override public void visit(ResourceVisitor rv) throws Exception { for (int i = 0; i < _list.size(); i++) { ResourceType rt = _list.get(i); rt.visit(rv); } } public void addNested(ResourcesDesc nested) throws Exception { if (nested != null) { nested.visit(new ResourceVisitor() { @Override public void visitJARDesc(JARDesc jd) { _list.add(jd); } @Override public void visitPropertyDesc(PropertyDesc pd) { _list.add(pd); } @Override public void visitExtensionDesc(ExtensionDesc ed) { _list.add(ed); } }); } } public static class JARDesc implements ResourceType { private URL _location; private String _locationString; private String _version; private boolean _isNativeLib; private boolean _isMainFile; // Only used for Java JAR files (a main JAR file is implicitly eager) private ResourcesDesc _parent; // Back-pointer to the Resources that contains this JAR public JARDesc(URL location, String version, boolean isMainFile, boolean isNativeLib, ResourcesDesc parent) { _location = location; _locationString = GeneralUtil.toNormalizedString(location); _version = version; _isMainFile = isMainFile; _isNativeLib = isNativeLib; _parent = parent; } /** * Type of JAR resource */ public boolean isNativeLib() { return _isNativeLib; } public boolean isJavaFile() { return !_isNativeLib; } /** * Returns URL/version for JAR file */ public URL getVersionLocation() throws Exception { if (getVersion() == null) { return _location; } else { return GeneralUtil.getEmbeddedVersionURL(getLocation(), getVersion()); } } public URL getLocation() { return _location; } public String getVersion() { return _version; } public String getName() { // File can be separated by '/' or '\\' int index; int index1 = _locationString.lastIndexOf('/'); int index2 = _locationString.lastIndexOf('\\'); if (index1 >= index2) { index = index1; } else { index = index2; } if (index != -1) { return _locationString.substring(index + 1, _locationString.length()); } return null; } /** * Returns if this is the main JAR file */ public boolean isMainJarFile() { return _isMainFile; } /** * Get parent LaunchDesc */ public ResourcesDesc getParent() { return _parent; } /** * Visitor dispatch */ public void visit(ResourceVisitor rv) { rv.visitJARDesc(this); } } public static class PropertyDesc implements ResourceType { private String _key; private String _value; public PropertyDesc(String key, String value) { _key = key; _value = value; } // Accessors public String getKey() { return _key; } public String getValue() { return _value; } /** * Visitor dispatch */ public void visit(ResourceVisitor rv) { rv.visitPropertyDesc(this); } } public static class JREDesc implements ResourceType { private String _version; private long _maxHeap; private long _minHeap; private String _vmargs; private ResourcesDesc _resourceDesc; private JNLPDesc _extensioDesc; private String _archList; /* * Constructor to create new instance based on the requirements from JNLP file. */ public JREDesc(String version, long minHeap, long maxHeap, String vmargs, ResourcesDesc resourcesDesc, String archList) { _version = version; _maxHeap = maxHeap; _minHeap = minHeap; _vmargs = vmargs; _resourceDesc = resourcesDesc; _extensioDesc = null; _archList = archList; } public String[] getArchList() { return GeneralUtil.getStringList(_archList); } public String getVersion() { return _version; } public long getMinHeap() { return _minHeap; } public long getMaxHeap() { return _maxHeap; } public String getVmArgs() { return _vmargs; } public String[] getVmArgsList() { return GeneralUtil.getStringList(_vmargs); } public ResourcesDesc getNestedResources() { return _resourceDesc; } public JNLPDesc getExtensionDesc() { return _extensioDesc; } public void setExtensionDesc(JNLPDesc ld) { _extensioDesc = ld; } /* visitor dispatch */ public void visit(ResourceVisitor rv) { rv.visitJREDesc(this); } } public static class Property implements Cloneable { public static final String JNLP_VERSION_ENABLED = "jnlp.versionEnabled"; String key; String value; public Property(String spec) { spec = spec.trim(); if (!spec.startsWith("-D") || spec.length() < 3) { throw new IllegalArgumentException("Property invalid"); } int endKey = spec.indexOf("="); if (endKey < 0) { // it's legal to have no assignment this.key = spec.substring(2); // skip "-D" this.value = ""; } else { this.key = spec.substring(2, endKey); this.value = spec.substring(endKey + 1); } } public static Property createProperty(String spec) { Property prop = null; try { prop = new Property(spec); } catch (IllegalArgumentException iae) { } return prop; } public Property(String key, String value) { this.key = key; if (value != null) { this.value = value; } else { this.value = ""; } } public String getKey() { return key; } public String getValue() { return value; } // @return String representation, unquoted, unified presentation public String toString() { if (value.length() == 0) { return "-D" + key; } return "-D" + key + "=" + value; } public void addTo(Properties props) { props.setProperty(key, value); } // Hash Object public boolean equals(Object o) { if (!(o instanceof Property)) { return false; } Property op = (Property) o; int hashTheirs = op.hashCode(); int hashThis = hashCode(); return hashTheirs == hashThis; } public int hashCode() { return key.hashCode(); } private static List jnlpProps = Arrays.asList(new Object[]{ JNLP_VERSION_ENABLED }); public static boolean isJnlpProperty(String spec) { try { Property p = new Property(spec); return isJnlpPropertyKey(p.getKey()); } catch (Exception e) { return false; } } public static boolean isJnlpPropertyKey(String key) { return key != null && jnlpProps.contains(key); } } public static class ExtensionDesc implements ResourceType { // Tag elements private final URL _location; private final String _locationString; private final String _version; private final URL _codebase; // Link to launchDesc private JNLPDesc _extensionLd; // Link to launchDesc for extension public ExtensionDesc(URL location, String version) { _location = location; _locationString = GeneralUtil.toNormalizedString(location); _version = version; _codebase = GeneralUtil.asPathURL(GeneralUtil.getBase(location)); _extensionLd = null; } public boolean isInstaller() throws Exception { if (getExtensionDesc() != null) { return _extensionLd.isInstaller(); } return false; } public URL getLocation() { return _location; } public String getVersionLocation() throws Exception { if (getVersion() == null) { return _locationString; } else { return GeneralUtil.toNormalizedString(GeneralUtil.getEmbeddedVersionURL(getLocation(), getVersion())); } } public String getVersion() { return _version; } public URL getCodebase() { return _codebase; } /* * Information about the resources */ public JNLPDesc getExtensionDesc() throws Exception { if (_extensionLd == null) { byte[] bits = HTTPHelper.getJNLPBits(getVersionLocation(), _locationString); _extensionLd = XMLFormat.parse(bits, getCodebase(), getVersionLocation()); } return _extensionLd; } public void setExtensionDesc(JNLPDesc desc) { _extensionLd = desc; } /** * Visitor dispatch */ public void visit(ResourceVisitor rv) throws Exception { rv.visitExtensionDesc(this); } } }