jnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/0000775000175000017500000000000012107707214020260 5ustar andrewandrewjnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/.gitignore0000664000175000017500000000001311710312010022223 0ustar andrewandrewclasses libjnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/.project0000664000175000017500000000056311710312010021714 0ustar andrewandrew jnlp-servlet org.eclipse.jdt.core.javabuilder org.eclipse.jdt.core.javanature jnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/.classpath0000664000175000017500000000045211710312010022225 0ustar andrewandrew jnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/example-web.xml0000664000175000017500000000230311710312010023167 0ustar andrewandrew JnlpDownloadServlet jnlp.sample.servlet.JnlpDownloadServlet JnlpDownloadServlet *.jnlp JnlpDownloadServlet *.jar jarDiffDisabled true Whether jar differencing is disabled or not packOnTheFly true Whether the servlet should attempt to create and save a pack200-gzip file on-the-fly packCommand pack200 -E7 --segment-limit=-1 The command and options that should be used for pack200. If using signed jars, the settings need to be identical to the ones used to pack/unpack (normalize) the jar before signing! jnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/README0000664000175000017500000000627511710312010021133 0ustar andrewandrewJNLPDownloadServlet ------------------- Brief Description : JNLPDownloadServlet can be used to package a JNLP file and its associated resources in a Web Archive (.war) file. The purpose of the servlet is to provide a simple and convenient packaging format for JNLP applications, so they can be easily deployed in a Web Container, such as Tomcat or a J2EE-compliant Application Server. The download servlet supports the following features: * Automatic installation of the codebase URL into JNLP files, thus eliminating manual management of hard-coded URLs into JNLP files. * Explicit specification of the timestamp for a JNLP file, independent of the file-system timestamp. * Support for download protocols defined in the JNLP specification v1.0.1. These include basic download protocol, version-based download protocol, and extension download protocol. * Version-based information specified per file or per directory in the Web archive. Thus, no centralized file needs to be managed for the entire archive. * Automatic generation of JARDiff files. * pack200-gzip and gzip compression support. You can now host *.jar.pack.gz or *.jar.gz files together with you original *.jar files. If the client supports the pack200-gzip or gzip file formats, the servlet will return the compressed file if it is available on the server. Java Web Start 1.5 supports both compression formats. The packaging support consists of one servlet: JnlpDownloadServlet. The servlet is packaged into the jnlp-servlet.jar file, which can be found in the SDK under samples/jnlp/servlet/. Files : GNUmakefile - the makefile to build the servlet jnlp-servlet.jar - the JNLPDownloadServlet binary jardiff.jar - the JarDiff binary jnlp.jar - the JNLP API binary src - the directory containing the JNLPDownloadServlet source code Building and Deployment of Servlet : To build jnlp-servlet.jar and jardiff.jar, run gnumake in the current directory (servlet). The following environment variables must be set: # Environment variable CLASS_PATH should contain the path to javaws.jar (under # the jre/lib directory) and servlet.jar (You can download the latter from # http://java.sun.com/products/servlet/, or you can find it in the lib # directory of any servlet container implementing the Java Servlet 2.2 # specification or above; e.g, Tomcat.) # # Environment variable FILE_SEPARATOR should be set: ";" on win32 and ":" on # unix). # # Environment variable TMPDIR should point to the tmp directory # # Environment variable SDK_HOME should point to the SDK directory If you are running on the windows platform, it is assumed you have the MKS Toolkit installed and it is in your path environment variable. (Commands such as cp, echo, mkdir, and rm are required.) The generated classes will go to the classes directory, and the resulting jar files will be in the lib directory by default. You can adjust the output directory to anything you want by changing the GNUmakefile. Please refer to the JNLPDownloadServlet guide for more information on the JNLPDownloadServlet: http://java.sun.com/j2se/1.5.0/docs/guide/javaws/developersguide/downloadservletguide.html jnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/GNUmakefile0000664000175000017500000000541611710312010022321 0ustar andrewandrew # environment variable FILE_SEPARATOR variable for file separator (; on win32 and : on unix) FILE_SEPARATOR = : # environment variable CLASS_PATH should contain path to javaws.jar and servlet.jar CLASS_PATH = $(JAVA_HOME)/lib/javaws.jar$(FILE_SEPARATOR)deplib/servlet-api.jar # environment variable TMPDIR should point to tmp directory # environment variable SDK_HOME should point to SDK directory SDK_HOME = $(JAVA_HOME) JAVAC = $(SDK_HOME)/bin/javac -source 1.5 -target 1.5 JAR = $(SDK_HOME)/bin/jar CLASSDESTDIR = classes MKDIR = mkdir -p RMDIR = rm -rf SRCDIR = src/classes COPY = cp -fr OUTPUT_DIR = lib CD = cd ECHO = echo FILES_servlet_java = \ jnlp/sample/servlet/JnlpDownloadServlet.java \ jnlp/sample/servlet/JnlpFileHandler.java \ jnlp/sample/servlet/DownloadRequest.java \ jnlp/sample/servlet/DownloadResponse.java \ jnlp/sample/servlet/ErrorResponseException.java \ jnlp/sample/servlet/JnlpResource.java \ jnlp/sample/servlet/ResourceCatalog.java \ jnlp/sample/servlet/JarDiffHandler.java \ jnlp/sample/servlet/XMLParsing.java \ jnlp/sample/servlet/XMLNode.java \ jnlp/sample/servlet/XMLAttribute.java \ jnlp/sample/servlet/Logger.java \ jnlp/sample/util/VersionID.java \ jnlp/sample/util/VersionString.java FILES_servlet_class = $(FILES_servlet_java:%.java=$(CLASSDESTDIR)/%.class) FILES_jardiff_java = \ jnlp/sample/jardiff/JarDiffConstants.java \ jnlp/sample/jardiff/JarDiff.java \ jnlp/sample/jardiff/Patcher.java \ jnlp/sample/jardiff/JarDiffPatcher.java FILES_jardiff_class = $(FILES_jardiff_java:%.java=$(CLASSDESTDIR)/%.class) $(CLASSDESTDIR)/%.class: $(SRCDIR)/%.java $(JAVAC) -classpath "$(CLASS_PATH)$(FILE_SEPARATOR)$(SRCDIR)" -d $(CLASSDESTDIR) $? all: prepare compile-jardiff jardiff-resource jardiff-jar compile-servlet servlet-resource servlet-jar clean: $(RMDIR) $(CLASSDESTDIR) $(RMDIR) $(OUTPUT_DIR) prepare: clean $(MKDIR) $(CLASSDESTDIR) $(MKDIR) $(OUTPUT_DIR) compile-jardiff: $(FILES_jardiff_class) compile-servlet: $(FILES_servlet_class) jardiff-resource: $(MKDIR) $(CLASSDESTDIR)/jnlp/sample/jardiff/resources/ $(COPY) $(SRCDIR)/jnlp/sample/jardiff/resources/strings.properties $(CLASSDESTDIR)/jnlp/sample/jardiff/resources/strings.properties servlet-resource: $(MKDIR) $(CLASSDESTDIR)/jnlp/sample/servlet/resources/ $(COPY) $(SRCDIR)/jnlp/sample/servlet/resources/strings.properties $(CLASSDESTDIR)/jnlp/sample/servlet/resources/strings.properties jardiff-jar: $(ECHO) 'Main-Class:' jnlp.sample.jardiff.JarDiff > $(TMPDIR)/jardiff.manifest $(JAR) cvfm $(OUTPUT_DIR)/jardiff.jar $(TMPDIR)/jardiff.manifest -C $(CLASSDESTDIR) . $(RM) $(TMPDIR)/jardiff.manifest servlet-jar: $(JAR) cvf $(OUTPUT_DIR)/jnlp-servlet.jar -C $(CLASSDESTDIR) . jnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/0000775000175000017500000000000011710312010021030 5ustar andrewandrewjnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/0000775000175000017500000000000011710312010022465 5ustar andrewandrewjnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/0000775000175000017500000000000011710312010023430 5ustar andrewandrewjnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/0000775000175000017500000000000011710312010024711 5ustar andrewandrewjnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/servlet/0000775000175000017500000000000011710312010026375 5ustar andrewandrewjnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/servlet/resources/0000775000175000017500000000000011710312010030407 5ustar andrewandrew././@LongLink0000000000000000000000000000016300000000000011565 Lustar rootrootjnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/servlet/resources/strings.propertiesjnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/servlet/resources/stri0000664000175000017500000000650311710312010031317 0ustar andrewandrew# # @(#)strings.properties 1.3 10/01/12 # # Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # -Redistribution of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # -Redistribution in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # Neither the name of Sun Microsystems, Inc. or the names of contributors may # be used to endorse or promote products derived from this software without # specific prior written permission. # # This software is provided "AS IS," without a warranty of any kind. ALL # EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING # ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE # OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") # AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE # AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS # DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST # REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, # INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY # OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, # EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. # # You acknowledge that this software is not designed, licensed or intended # for use in the design, construction, operation or maintenance of any # nuclear facility. # # Fatals servlet.log.fatal.internalerror=Internal error: # Warnings servlet.log.warning.nolastmodified=Last-modified read as 0 for {0} servlet.log.warning.notimestamp=Timestamp read as 0 for {0}. Using Last-modified instead servlet.log.warning.missing-file=Reference to non-existing file ({0}) in {1} servlet.log.warning.xml.parsing=Error parsing {0} at line {1}: {2} servlet.log.warning.xml.reading=Unexpected error reading {0}: servlet.log.warning.xml.missing-jnlp=Missing element in {0} servlet.log.warning.xml.missing-pattern=Missing element in {0} servlet.log.warning.xml.missing-elems=Missing or attribute in {0} servlet.log.warning.xml.missing-elems2=Missing , , or attribute in {0} servlet.log.warning.jardiff.failed=Failed to generate JarDiff for {0} {1}->{2} # Informational servlet.log.info.request=Request: {0} servlet.log.info.useragent=User-Agent: {0} servlet.log.info.goodrequest=Resource returned: {0} servlet.log.info.badrequest=Error code returned for request: {0} servlet.log.scandir=Rescanning directory: {0} servlet.log.info.jardiff.response=JarDiff returned for request servlet.log.info.jardiff.gen=Generating JarDiff for {0} {1}->{2} # JNLP Error strings servlet.jnlp.err.10 = Could not locate resource servlet.jnlp.err.11 = Could not locate requested version servlet.jnlp.err.20 = Unsupported operating system servlet.jnlp.err.21 = Unsupported architecture servlet.jnlp.err.22 = Unsupported locale servlet.jnlp.err.23 = Unsupported JRE version servlet.jnlp.err.99 = Unknown error jnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/servlet/XMLNode.java0000664000175000017500000001251211710312010030507 0ustar andrewandrew/* * @(#)XMLNode.java 1.7 10/01/12 * * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ package jnlp.sample.servlet; import java.io.PrintWriter; import java.io.StringWriter; /** Class that contains information about an XML Node */ public class XMLNode { private boolean _isElement; // Element/PCTEXT private String _name; private XMLAttribute _attr; private XMLNode _parent; // Parent Node private XMLNode _nested; // Nested XML tags private XMLNode _next; // Following XML tag on the same level /** Creates a PCTEXT node */ public XMLNode(String name) { this(name, null, null, null); _isElement = false; } /** Creates a ELEMENT node */ public XMLNode(String name, XMLAttribute attr) { this(name, attr, null, null); } /** Creates a ELEMENT node */ public XMLNode(String name, XMLAttribute attr, XMLNode nested, XMLNode next) { _isElement = true; _name = name; _attr = attr; _nested = nested; _next = next; _parent = null; } public String getName() { return _name; } public XMLAttribute getAttributes() { return _attr; } public XMLNode getNested() { return _nested; } public XMLNode getNext() { return _next; } public boolean isElement() { return _isElement; } public void setParent(XMLNode parent) { _parent = parent; } public XMLNode getParent() { return _parent; } public void setNext(XMLNode next) { _next = next; } public void setNested(XMLNode nested) { _nested = nested; } public boolean equals(Object o) { if (o == null || !(o instanceof XMLNode)) return false; XMLNode other = (XMLNode)o; boolean result = match(_name, other._name) && match(_attr, other._attr) && match(_nested, other._nested) && match(_next, other._next); return result; } public String getAttribute(String name) { XMLAttribute cur = _attr; while(cur != null) { if (name.equals(cur.getName())) return cur.getValue(); cur = cur.getNext(); } return ""; } private static boolean match(Object o1, Object o2) { if (o1 == null) return (o2 == null); return o1.equals(o2); } public void printToStream(PrintWriter out) { printToStream(out, 0); } public void printToStream(PrintWriter out, int n) { if (!isElement()) { out.print(_name); } else { if (_nested == null) { String attrString = (_attr == null) ? "" : (" " + _attr.toString()); lineln(out, n, "<" + _name + attrString + "/>"); } else { String attrString = (_attr == null) ? "" : (" " + _attr.toString()); lineln(out, n, "<" + _name + attrString + ">"); _nested.printToStream(out, n + 1); if (_nested.isElement()) { lineln(out, n, ""); } else { out.print(""); } } } if (_next != null) { _next.printToStream(out, n); } } private static void lineln(PrintWriter out, int indent, String s) { out.println(""); for(int i = 0; i < indent; i++) { out.print(" "); } out.print(s); } public String toString() { StringWriter sw = new StringWriter(1000); PrintWriter pw = new PrintWriter(sw); printToStream(pw); pw.close(); return sw.toString(); } } ././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootjnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/servlet/JnlpDownloadServlet.javajnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/servlet/JnlpDownloadSe0000664000175000017500000002655411710312010031217 0ustar andrewandrew/* * @(#)JnlpDownloadServlet.java 1.11 10/01/12 * * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ package jnlp.sample.servlet; import java.io.IOException; import java.util.ResourceBundle; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * This Servlet class is an implementation of JNLP Specification's Download * Protocols. * * All requests to this servlet is in the form of HTTP GET commands. The * parameters that are needed are: *
    *
  • arch, *
  • os, *
  • locale, *
  • version-id or platform-version-id, *
  • current-version-id, *
  • known-platforms *
*

* * @version 1.8 01/23/03 */ public class JnlpDownloadServlet extends HttpServlet { // Localization private static ResourceBundle _resourceBundle = null; // Servlet configuration private static final String PARAM_JNLP_EXTENSION = "jnlp-extension"; private static final String PARAM_JAR_EXTENSION = "jar-extension"; // Servlet configuration private Logger _log = null; private JnlpFileHandler _jnlpFileHandler = null; private JarDiffHandler _jarDiffHandler = null; private ResourceCatalog _resourceCatalog = null; /** Initialize servlet */ @Override public void init(ServletConfig config) throws ServletException { super.init(config); // Setup logging _log = new Logger(config, getResourceBundle()); _log.addDebug("Initializing"); // Get extension from Servlet configuration, or use default JnlpResource.setDefaultExtensions(config.getInitParameter(PARAM_JNLP_EXTENSION), config .getInitParameter(PARAM_JAR_EXTENSION)); _jnlpFileHandler = new JnlpFileHandler(config.getServletContext(), _log); _jarDiffHandler = new JarDiffHandler(config.getServletContext(), _log); _resourceCatalog = new ResourceCatalog(config.getServletContext(), _log); } public static synchronized ResourceBundle getResourceBundle() { if (_resourceBundle == null) { _resourceBundle = ResourceBundle.getBundle("jnlp/sample/servlet/resources/strings"); } return _resourceBundle; } @Override public void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { handleRequest(request, response, true); } /** * We handle get requests too - eventhough the spec. only requeres POST * requests */ @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { handleRequest(request, response, false); } private void handleRequest(HttpServletRequest request, HttpServletResponse response, boolean isHead) throws IOException { String requestStr = request.getRequestURI(); if (request.getQueryString() != null) requestStr += "?" + request.getQueryString().trim(); // Parse HTTP request DownloadRequest dreq = new DownloadRequest(getServletContext(), request); if (_log.isInformationalLevel()) { _log.addInformational("servlet.log.info.request", requestStr); _log.addInformational("servlet.log.info.useragent", request.getHeader("User-Agent")); } if (_log.isDebugLevel()) { _log.addDebug(dreq.toString()); } long ifModifiedSince = request.getDateHeader("If-Modified-Since"); // Check if it is a valid request try { // Check if the request is valid validateRequest(dreq); // Decide what resource to return JnlpResource jnlpres = locateResource(dreq); _log.addDebug("JnlpResource: " + jnlpres); if (_log.isInformationalLevel()) { _log.addInformational("servlet.log.info.goodrequest", jnlpres.getPath()); } DownloadResponse dres = null; if (isHead) { int cl = jnlpres.getResource().openConnection().getContentLength(); // head request response dres = DownloadResponse.getHeadRequestResponse(jnlpres.getMimeType(), jnlpres.getVersionId(), jnlpres .getLastModified(), cl); } else if (ifModifiedSince != -1 && (ifModifiedSince / 1000) >= (jnlpres.getLastModified() / 1000)) { // We divide the value returned by getLastModified here by // 1000 // because if protocol is HTTP, last 3 digits will always be // zero. However, if protocol is JNDI, that's not the case. // so we divide the value by 1000 to remove the last 3 // digits // before comparison // return 304 not modified if possible _log.addDebug("return 304 Not modified"); dres = DownloadResponse.getNotModifiedResponse(); } else { // Return selected resource dres = constructResponse(jnlpres, dreq); } dres.sendRespond(response); } catch (ErrorResponseException ere) { if (_log.isInformationalLevel()) { _log.addInformational("servlet.log.info.badrequest", requestStr); } if (_log.isDebugLevel()) { _log.addDebug("Response: " + ere.toString()); } // Return response from exception ere.getDownloadResponse().sendRespond(response); } catch (Throwable e) { _log.addFatal("servlet.log.fatal.internalerror", e); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } /** * Make sure that it is a valid request. This is also the place to implement * the reverse IP lookup */ private void validateRequest(DownloadRequest dreq) throws ErrorResponseException { String path = dreq.getPath(); if (path.endsWith(ResourceCatalog.VERSION_XML_FILENAME) || path.indexOf("__") != -1) { throw new ErrorResponseException( DownloadResponse.getNoContentResponse()); } } /** * Interprets the download request and convert it into a resource that is * part of the Web Archive. */ private JnlpResource locateResource(DownloadRequest dreq) throws IOException, ErrorResponseException { if (dreq.getVersion() == null) { return handleBasicDownload(dreq); } else { return handleVersionRequest(dreq); } } private JnlpResource handleBasicDownload(DownloadRequest dreq) throws ErrorResponseException, IOException { _log.addDebug("Basic Protocol lookup"); // Do not return directory names for basic protocol if (dreq.getPath() == null || dreq.getPath().endsWith("/")) { throw new ErrorResponseException(DownloadResponse .getNoContentResponse()); } // Lookup resource JnlpResource jnlpres = new JnlpResource(getServletContext(), dreq.getPath()); if (!jnlpres.exists()) { throw new ErrorResponseException(DownloadResponse.getNoContentResponse()); } return jnlpres; } private JnlpResource handleVersionRequest(DownloadRequest dreq) throws IOException, ErrorResponseException { _log.addDebug("Version-based/Extension based lookup"); return _resourceCatalog.lookupResource(dreq); } /** * Given a DownloadPath and a DownloadRequest, it constructs the data stream * to return to the requester */ private DownloadResponse constructResponse(JnlpResource jnlpres, DownloadRequest dreq) throws IOException { String path = jnlpres.getPath(); if (jnlpres.isJnlpFile()) { // It is a JNLP file. It need to be macro-expanded, so it is handled // differently boolean supportQuery = JarDiffHandler.isJavawsVersion(dreq, "1.5+"); _log.addDebug("SupportQuery in Href: " + supportQuery); // only support query string in href for 1.5 and above if (supportQuery) { return _jnlpFileHandler.getJnlpFileEx(jnlpres, dreq); } else { return _jnlpFileHandler.getJnlpFile(jnlpres, dreq); } } // Check if a JARDiff can be returned if (jarDiffEnabled() && dreq.getCurrentVersionId() != null && jnlpres.isJarFile()) { DownloadResponse response = _jarDiffHandler.getJarDiffEntry(_resourceCatalog, dreq, jnlpres); if (response != null) { _log.addInformational("servlet.log.info.jardiff.response"); return response; } } // check and see if we can use pack resource JnlpResource jr = new JnlpResource(getServletContext(), jnlpres.getName(), jnlpres.getVersionId(), jnlpres .getOSList(), jnlpres.getArchList(), jnlpres.getLocaleList(), jnlpres.getPath(), jnlpres .getReturnVersionId(), dreq.getEncoding()); _log.addDebug("Real resource returned: " + jr); // Return WAR file resource return DownloadResponse.getFileDownloadResponse(jr.getResource(), jr.getMimeType(), jr.getLastModified(), jr .getReturnVersionId()); } private boolean jarDiffEnabled() { ServletContext context = getServletContext(); if (context == null) { return true; } return !Boolean.valueOf(context.getInitParameter("jarDiffDisabled")); } } jnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/servlet/Logger.java0000664000175000017500000001713411710312010030465 0ustar andrewandrew/* * @(#)Logger.java 1.7 10/01/12 * * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ package jnlp.sample.servlet; import java.text.MessageFormat; import java.util.*; import java.io.*; import javax.servlet.*; /* A loging object used by the servlets */ public class Logger { // Logging levels public final static int NONE = 0; public static final String NONE_KEY = "NONE"; public final static int FATAL = 1; public static final String FATAL_KEY = "FATAL"; public final static int WARNING = 2; public static final String WARNING_KEY = "WARNING"; public final static int INFORMATIONAL = 3; public static final String INFORMATIONAL_KEY = "INFORMATIONAL"; public final static int DEBUG = 4; public static final String DEBUG_KEY = "DEBUG"; // Configuration parameters private final static String LOG_LEVEL = "logLevel"; private final static String LOG_PATH = "logPath"; private int _loggingLevel = FATAL; private ServletContext _servletContext = null; private String _logFile = null; private String _servletName = null; // Localization ResourceBundle _resources = null; /** Initialize logging object. It reads the logLevel and pathLevel init parameters. * Default is logging level FATAL, and logging using the ServletContext.log */ public Logger(ServletConfig config, ResourceBundle resources) { _resources = resources; _servletContext = config.getServletContext(); _servletName = config.getServletName(); _logFile = config.getInitParameter(LOG_PATH); if (_logFile != null) { _logFile = _logFile.trim(); if (_logFile.length() == 0) _logFile = null; } String level = config.getInitParameter(LOG_LEVEL); if (level != null) { level = level.trim().toUpperCase(); if (level.equals(NONE_KEY)) _loggingLevel = NONE; if (level.equals(FATAL_KEY)) _loggingLevel = FATAL; if (level.equals(WARNING_KEY)) _loggingLevel = WARNING; if (level.equals(INFORMATIONAL_KEY)) _loggingLevel = INFORMATIONAL; if (level.equals(DEBUG_KEY)) _loggingLevel = DEBUG; } } // Logging API. Fatal, Warning, and Informational are localized public void addFatal(String key, Throwable throwable) { logEvent(FATAL, getString(key), throwable); } public void addWarning(String key, String arg) { logL10N(WARNING, key, arg, (Throwable)null); } public void addWarning(String key, String arg, Throwable t) { logL10N(WARNING, key, arg, t); } public void addWarning(String key, String arg1, String arg2) { logL10N(WARNING, key, arg1, arg2); } public void addWarning(String key, String arg1, String arg2, String arg3) { logL10N(WARNING, key, arg1, arg2, arg3); } public void addInformational(String key) { logEvent(INFORMATIONAL, getString(key), (Throwable)null); } public void addInformational(String key, String arg) { logL10N(INFORMATIONAL, key, arg, (Throwable)null); } public void addInformational(String key, String arg1, String arg2, String arg3) { logL10N(INFORMATIONAL, key, arg1, arg2, arg3); } // Debug messages are not localized public void addDebug(String msg) { logEvent(DEBUG, msg, null); } public void addDebug(String msg, Throwable throwable) { logEvent(DEBUG, msg, throwable); } // Query to test for level boolean isNoneLevel() { return _loggingLevel >= NONE; } boolean isFatalevel() { return _loggingLevel >= FATAL; } boolean isWarningLevel() { return _loggingLevel >= WARNING; } boolean isInformationalLevel() { return _loggingLevel >= INFORMATIONAL; } boolean isDebugLevel() { return _loggingLevel >= DEBUG; } // Returns a string from the resources private String getString(String key) { try { return _resources.getString(key); } catch (MissingResourceException mre) { return "Missing resource for: " + key; } } private void logL10N(int level, String key, String arg, Throwable e) { Object[] messageArguments = { arg }; logEvent(level, applyPattern(key, messageArguments), e); } private void logL10N(int level, String key, String arg1, String arg2) { Object[] messageArguments = { arg1, arg2 }; logEvent(level, applyPattern(key, messageArguments), null); } private void logL10N(int level, String key, String arg1, String arg2, String arg3) { Object[] messageArguments = { arg1, arg2, arg3 }; logEvent(level, applyPattern(key, messageArguments), null); } /** Helper function that applies the messageArguments to a message from the resource object */ private String applyPattern(String key, Object[] messageArguments) { String message = getString(key); MessageFormat formatter = new MessageFormat(message); String output = formatter.format(message, messageArguments); return output; } // The method that actually does the logging */ private synchronized void logEvent(int level, String string, Throwable throwable) { // Check if the event should be logged if (level > _loggingLevel) return; if (_logFile != null) { // No logfile specified, log using servlet context PrintWriter pw = null; try { pw = new PrintWriter(new FileWriter(_logFile, true)); pw.println(_servletName + "(" + level + "): " + string); if (throwable != null) { throwable.printStackTrace(pw); } pw.close(); // Do a return here. An exception will cause a fall through to // do _servletContex logging API return; } catch (IOException ioe) { /* just ignore */ } } // Otherwise, write to servlet context log if (throwable == null) { _servletContext.log(string); } else { _servletContext.log(string, throwable); } } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/servlet/JnlpFileHandler.javajnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/servlet/JnlpFileHandle0000664000175000017500000004166211710312010031150 0ustar andrewandrew/* * @(#)JnlpFileHandler.java 1.13 10/01/12 * * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ package jnlp.sample.servlet; import java.util.*; import java.util.regex.*; import java.net.*; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import javax.xml.parsers.*; import org.xml.sax.*; import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.*; /* The JNLP file handler implements a class that keeps * track of JNLP files and their specializations */ public class JnlpFileHandler { private static final String JNLP_MIME_TYPE = "application/x-java-jnlp-file"; private static final String HEADER_LASTMOD = "Last-Modified"; private ServletContext _servletContext; private Logger _log = null; private HashMap _jnlpFiles = null; /** Initialize JnlpFileHandler for the specific ServletContext */ public JnlpFileHandler(ServletContext servletContext, Logger log) { _servletContext = servletContext; _log = log; _jnlpFiles = new HashMap(); } private static class JnlpFileEntry { // Response DownloadResponse _response; // Keeps track of cache is out of date private long _lastModified; // Constructor JnlpFileEntry(DownloadResponse response, long lastmodfied) { _response = response; _lastModified = lastmodfied; } public DownloadResponse getResponse() { return _response; } long getLastModified() { return _lastModified; } } /* Main method to lookup an entry */ public synchronized DownloadResponse getJnlpFile(JnlpResource jnlpres, DownloadRequest dreq) throws IOException { String path = jnlpres.getPath(); URL resource = jnlpres.getResource(); long lastModified = jnlpres.getLastModified(); _log.addDebug("lastModified: " + lastModified + " " + new Date(lastModified)); if (lastModified == 0) { _log.addWarning("servlet.log.warning.nolastmodified", path); } // fix for 4474854: use the request URL as key to look up jnlp file // in hash map String reqUrl = HttpUtils.getRequestURL(dreq.getHttpRequest()).toString(); // Check if entry already exist in HashMap JnlpFileEntry jnlpFile = (JnlpFileEntry)_jnlpFiles.get(reqUrl); if (jnlpFile != null && jnlpFile.getLastModified() == lastModified) { // Entry found in cache, so return it return jnlpFile.getResponse(); } // Read information from WAR file long timeStamp = lastModified; String mimeType = _servletContext.getMimeType(path); if (mimeType == null) mimeType = JNLP_MIME_TYPE; StringBuffer jnlpFileTemplate = new StringBuffer(); URLConnection conn = resource.openConnection(); BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8")); String line = br.readLine(); if (line != null && line.startsWith("TS:")) { timeStamp = parseTimeStamp(line.substring(3)); _log.addDebug("Timestamp: " + timeStamp + " " + new Date(timeStamp)); if (timeStamp == 0) { _log.addWarning("servlet.log.warning.notimestamp", path); timeStamp = lastModified; } line = br.readLine(); } while(line != null) { jnlpFileTemplate.append(line); line = br.readLine(); } String jnlpFileContent = specializeJnlpTemplate(dreq.getHttpRequest(), path, jnlpFileTemplate.toString()); // Convert to bytes as a UTF-8 encoding byte[] byteContent = jnlpFileContent.getBytes("UTF-8"); // Create entry DownloadResponse resp = DownloadResponse.getFileDownloadResponse(byteContent, mimeType, timeStamp, jnlpres.getReturnVersionId()); jnlpFile = new JnlpFileEntry(resp, lastModified); _jnlpFiles.put(reqUrl, jnlpFile); return resp; } /* Main method to lookup an entry (NEW for JavaWebStart 1.5+) */ public synchronized DownloadResponse getJnlpFileEx(JnlpResource jnlpres, DownloadRequest dreq) throws IOException { String path = jnlpres.getPath(); URL resource = jnlpres.getResource(); long lastModified = jnlpres.getLastModified(); _log.addDebug("lastModified: " + lastModified + " " + new Date(lastModified)); if (lastModified == 0) { _log.addWarning("servlet.log.warning.nolastmodified", path); } // fix for 4474854: use the request URL as key to look up jnlp file // in hash map String reqUrl = HttpUtils.getRequestURL(dreq.getHttpRequest()).toString(); // SQE: To support query string, we changed the hash key from Request URL to (Request URL + query string) if (dreq.getQuery() != null) reqUrl += dreq.getQuery(); // Check if entry already exist in HashMap JnlpFileEntry jnlpFile = (JnlpFileEntry)_jnlpFiles.get(reqUrl); if (jnlpFile != null && jnlpFile.getLastModified() == lastModified) { // Entry found in cache, so return it return jnlpFile.getResponse(); } // Read information from WAR file long timeStamp = lastModified; String mimeType = _servletContext.getMimeType(path); if (mimeType == null) mimeType = JNLP_MIME_TYPE; StringBuffer jnlpFileTemplate = new StringBuffer(); URLConnection conn = resource.openConnection(); BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8")); String line = br.readLine(); if (line != null && line.startsWith("TS:")) { timeStamp = parseTimeStamp(line.substring(3)); _log.addDebug("Timestamp: " + timeStamp + " " + new Date(timeStamp)); if (timeStamp == 0) { _log.addWarning("servlet.log.warning.notimestamp", path); timeStamp = lastModified; } line = br.readLine(); } while(line != null) { jnlpFileTemplate.append(line); line = br.readLine(); } String jnlpFileContent = specializeJnlpTemplate(dreq.getHttpRequest(), path, jnlpFileTemplate.toString()); /* SQE: We need to add query string back to href in jnlp file. We also need to handle JRE requirement for * the test. We reconstruct the xml DOM object, modify the value, then regenerate the jnlpFileContent. */ String query = dreq.getQuery(); String testJRE = dreq.getTestJRE(); _log.addDebug("Double check query string: " + query); // For backward compatibility: Always check if the href value exists. // Bug 4939273: We will retain the jnlp template structure and will NOT add href value. Above old // approach to always check href value caused some test case not run. if (query != null) { byte [] cb = jnlpFileContent.getBytes("UTF-8"); ByteArrayInputStream bis = new ByteArrayInputStream(cb); try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(bis); if (document != null && document.getNodeType() == Node.DOCUMENT_NODE) { boolean modified = false; Element root = document.getDocumentElement(); if (root.hasAttribute("href") && query != null) { String href = root.getAttribute("href"); root.setAttribute("href", href + "?" + query); modified = true; } // Update version value for j2se tag if (testJRE != null) { NodeList j2seNL = root.getElementsByTagName("j2se"); if (j2seNL != null) { Element j2se = (Element) j2seNL.item(0); String ver = j2se.getAttribute("version"); if (ver.length() > 0) { j2se.setAttribute("version", testJRE); modified = true; } } } TransformerFactory tFactory = TransformerFactory.newInstance(); Transformer transformer = tFactory.newTransformer(); DOMSource source = new DOMSource(document); StringWriter sw = new StringWriter(); StreamResult result = new StreamResult(sw); transformer.transform(source, result); jnlpFileContent = sw.toString(); _log.addDebug("Converted jnlpFileContent: " + jnlpFileContent); // Since we modified the file on the fly, we always update the timestamp value with current time if (modified) { timeStamp = new java.util.Date().getTime(); _log.addDebug("Last modified on the fly: " + timeStamp); } } } catch (Exception e) { _log.addDebug(e.toString(), e); } } // Convert to bytes as a UTF-8 encoding byte[] byteContent = jnlpFileContent.getBytes("UTF-8"); // Create entry DownloadResponse resp = DownloadResponse.getFileDownloadResponse(byteContent, mimeType, timeStamp, jnlpres.getReturnVersionId()); jnlpFile = new JnlpFileEntry(resp, lastModified); _jnlpFiles.put(reqUrl, jnlpFile); return resp; } /* This method performs the following substituations * $$name * $$codebase * $$context */ private String specializeJnlpTemplate(HttpServletRequest request, String respath, String jnlpTemplate) { String urlprefix = getUrlPrefix(request); int idx = respath.lastIndexOf('/'); // String name = respath.substring(idx + 1); // Exclude / String codebase = respath.substring(0, idx + 1); // Include / jnlpTemplate = substitute(jnlpTemplate, "$$name", name); // fix for 5039951: Add $$hostname macro jnlpTemplate = substitute(jnlpTemplate, "$$hostname", request.getServerName()); jnlpTemplate = substitute(jnlpTemplate, "$$codebase", urlprefix + request.getContextPath() + codebase); jnlpTemplate = substitute(jnlpTemplate, "$$context", urlprefix + request.getContextPath()); // fix for 6256326: add $$site macro to sample jnlp servlet jnlpTemplate = substitute(jnlpTemplate, "$$site", urlprefix); return jnlpTemplate; } // This code is heavily inspired by the stuff in HttpUtils.getRequestURL private String getUrlPrefix(HttpServletRequest req) { StringBuffer url = new StringBuffer(); String scheme = req.getScheme(); int port = req.getServerPort(); url.append(scheme); // http, https url.append("://"); url.append(req.getServerName()); if ((scheme.equals("http") && port != 80) || (scheme.equals("https") && port != 443)) { url.append(':'); url.append(req.getServerPort()); } return url.toString(); } private String substitute(String target, String key, String value) { int start = 0; do { int idx = target.indexOf(key, start); if (idx == -1) return target; target = target.substring(0, idx) + value + target.substring(idx + key.length()); start = idx + value.length(); } while(true); } /** Parses a ISO 8601 Timestamp. The format of the timestamp is: * * YYYY-MM-DD hh:mm:ss or YYYYMMDDhhmmss * * Hours (hh) is in 24h format. ss are optional. Time are by default relative * to the current timezone. Timezone information can be specified * by: * * - Appending a 'Z', e.g., 2001-12-19 12:00Z * - Appending +hh:mm, +hhmm, +hh, -hh:mm -hhmm, -hh to * indicate that the locale timezone used is either the specified * amound before or after GMT. For example, * * 12:00Z = 13:00+1:00 = 0700-0500 * * The method returns 0 if it cannot pass the string. Otherwise, it is * the number of milliseconds size sometime in 1969. */ private long parseTimeStamp(String timestamp) { int YYYY = 0; int MM = 0; int DD = 0; int hh = 0; int mm = 0; int ss = 0; timestamp = timestamp.trim(); try { // Check what format is used if (matchPattern("####-##-## ##:##", timestamp)) { YYYY = getIntValue(timestamp, 0, 4); MM = getIntValue(timestamp, 5, 7); DD = getIntValue(timestamp, 8, 10); hh = getIntValue(timestamp, 11, 13); mm = getIntValue(timestamp, 14, 16); timestamp = timestamp.substring(16); if (matchPattern(":##", timestamp)) { ss = getIntValue(timestamp, 1, 3); timestamp = timestamp.substring(3); } } else if (matchPattern("############", timestamp)) { YYYY = getIntValue(timestamp, 0, 4); MM = getIntValue(timestamp, 4, 6); DD = getIntValue(timestamp, 6, 8); hh = getIntValue(timestamp, 8, 10); mm = getIntValue(timestamp, 10, 12); timestamp = timestamp.substring(12); if (matchPattern("##", timestamp)) { ss = getIntValue(timestamp, 0, 2); timestamp = timestamp.substring(2); } } else { // Unknown format return 0; } } catch(NumberFormatException e) { // Bad number return 0; } String timezone = null; // Remove timezone information timestamp = timestamp.trim(); if (timestamp.equalsIgnoreCase("Z")) { timezone ="GMT"; } else if (timestamp.startsWith("+") || timestamp.startsWith("-")) { timezone = "GMT" + timestamp; } if (timezone == null) { // Date is relative to current locale Calendar cal = Calendar.getInstance(); cal.set(YYYY, MM - 1, DD, hh, mm, ss); return cal.getTime().getTime(); } else { // Date is relative to a timezone Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(timezone)); cal.set(YYYY, MM - 1, DD, hh, mm, ss); return cal.getTime().getTime(); } } private int getIntValue(String key, int start, int end) { return Integer.parseInt(key.substring(start, end)); } private boolean matchPattern(String pattern, String key) { // Key must be longer than pattern if (key.length() < pattern.length()) return false; for(int i = 0; i < pattern.length(); i++) { char format = pattern.charAt(i); char ch = key.charAt(i); if (!((format == '#' && Character.isDigit(ch)) || (format == ch))) { return false; } } return true; } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/servlet/DownloadRequest.javajnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/servlet/DownloadReques0000664000175000017500000002153611710312010031263 0ustar andrewandrew/* * @(#)DownloadRequest.java 1.8 10/01/12 * * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ package jnlp.sample.servlet; import java.io.File; import java.util.ArrayList; import javax.servlet.*; import javax.servlet.http.*; /** * The DownloadRequest incapsulates all the data in a request * SQE: We need to address query string */ public class DownloadRequest { // Arguments private static final String ARG_ARCH = "arch"; private static final String ARG_OS = "os"; private static final String ARG_LOCALE = "locale"; private static final String ARG_VERSION_ID = "version-id"; private static final String ARG_CURRENT_VERSION_ID = "current-version-id"; private static final String ARG_PLATFORM_VERSION_ID = "platform-version-id"; private static final String ARG_KNOWN_PLATFORMS = "known-platforms"; private static final String TEST_JRE = "TestJRE"; private String _path = null; private String _version = null; private String _currentVersionId = null; private String[] _os = null; private String[] _arch = null; private String[] _locale = null; private String[] _knownPlatforms = null; private String _query = null; private String _testJRE = null; private boolean _isPlatformRequest = false; private ServletContext _context = null; private String _encoding = null; private HttpServletRequest _httpRequest = null; // HTTP Compression RFC 2616 : Standard headers public static final String ACCEPT_ENCODING = "accept-encoding"; // Contruct Request object based on HTTP request public DownloadRequest(HttpServletRequest request) { this((ServletContext)null, request); } public DownloadRequest(ServletContext context, HttpServletRequest request) { _context = context; _httpRequest = request; _path = request.getRequestURI(); _encoding = request.getHeader(ACCEPT_ENCODING); String context_path = request.getContextPath(); if (context_path != null) _path = _path.substring(context_path.length()); if (_path == null) _path = request.getServletPath(); // This works for *. invocations if (_path == null) _path = "/"; // No path given _path = _path.trim(); if (_context != null && !_path.endsWith("/")) { String realPath = _context.getRealPath(_path); // fix for 4474021 - getRealPath might returns NULL if (realPath != null) { File f = new File(realPath); if (f != null && f.exists() && f.isDirectory()) { _path += "/"; } } } // Append default file for a directory if (_path.endsWith("/")) _path += "launch.jnlp"; _version = getParameter(request, ARG_VERSION_ID); _currentVersionId = getParameter(request, ARG_CURRENT_VERSION_ID); _os = getParameterList(request, ARG_OS); _arch = getParameterList(request, ARG_ARCH); _locale = getParameterList(request, ARG_LOCALE); _knownPlatforms = getParameterList(request, ARG_KNOWN_PLATFORMS); String platformVersion = getParameter(request, ARG_PLATFORM_VERSION_ID); _isPlatformRequest = (platformVersion != null); if (_isPlatformRequest) _version = platformVersion; _query = request.getQueryString(); _testJRE = getParameter(request, TEST_JRE); } /** Returns a DownloadRequest for the currentVersionId, that can be used * to lookup the existing cached version */ private DownloadRequest(DownloadRequest dreq) { _encoding = dreq._encoding; _context = dreq._context; _httpRequest = dreq._httpRequest; _path = dreq._path; _version = dreq._currentVersionId; _currentVersionId = null; _os = dreq._os; _arch = dreq._arch; _locale = dreq._locale; _knownPlatforms = dreq._knownPlatforms; _isPlatformRequest = dreq._isPlatformRequest; _query = dreq._query; _testJRE = dreq._testJRE; } private String getParameter(HttpServletRequest req, String key) { String res = req.getParameter(key); return (res == null) ? null : res.trim(); } /** Converts a space delimitered string to a list of strings */ static private String[] getStringList(String str) { if (str == null) return null; ArrayList list = new ArrayList(); int i = 0; int length = str.length(); StringBuffer sb = null; while(i < length) { char ch = str.charAt(i); if (ch == ' ') { // A space was hit. Add string to list if (sb != null) { list.add(sb.toString()); sb = null; } } else if (ch == '\\') { // It is a delimiter. Add next character if (i + 1 < length) { ch = str.charAt(++i); if (sb == null) sb = new StringBuffer(); sb.append(ch); } } else { if (sb == null) sb = new StringBuffer(); sb.append(ch); } i++; // Next character } // Make sure to add the last part to the list too if (sb != null) { list.add(sb.toString()); } if (list.size() == 0) return null; String[] results = new String[list.size()]; return (String[])list.toArray(results); } /* Split parameter at spaces. Convert '\ ' insto a space */ private String[] getParameterList(HttpServletRequest req, String key) { String res = req.getParameter(key); return (res == null) ? null : getStringList(res.trim()); } // Query public String getPath() { return _path; } public String getVersion() { return _version; } public String getCurrentVersionId() { return _currentVersionId; } public String getQuery() { return _query; } public String getTestJRE() { return _testJRE; } public String getEncoding() { return _encoding; } public String[] getOS() { return _os; } public String[] getArch() { return _arch; } public String[] getLocale() { return _locale; } public String[] getKnownPlatforms() { return _knownPlatforms; } public boolean isPlatformRequest() { return _isPlatformRequest; } public HttpServletRequest getHttpRequest() { return _httpRequest; } /** Returns a DownloadRequest for the currentVersionId, that can be used * to lookup the existing cached version */ DownloadRequest getFromDownloadRequest() { return new DownloadRequest(this); } // Debug public String toString() { return "DownloadRequest[path=" + _path + showEntry(" encoding=", _encoding) + showEntry(" query=", _query) + showEntry(" TestJRE=", _testJRE) + showEntry(" version=", _version) + showEntry(" currentVersionId=", _currentVersionId) + showEntry(" os=", _os) + showEntry(" arch=", _arch) + showEntry(" locale=", _locale) + showEntry(" knownPlatforms=", _knownPlatforms) + " isPlatformRequest=" + _isPlatformRequest + "]"; } private String showEntry(String msg, String value) { if (value == null) return ""; return msg + value; } private String showEntry(String msg, String[] value) { if (value == null) return ""; return msg + java.util.Arrays.asList(value).toString(); } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/servlet/ResourceCatalog.javajnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/servlet/ResourceCatalo0000664000175000017500000004303511710312010031240 0ustar andrewandrew/* * @(#)ResourceCatalog.java 1.7 10/01/12 * * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ package jnlp.sample.servlet; import java.util.HashMap; import java.util.List; import java.util.ArrayList; import java.io.File; import java.io.BufferedInputStream; import javax.servlet.ServletContext; import javax.xml.parsers.*; import org.xml.sax.*; import org.w3c.dom.*; import jnlp.sample.util.VersionString; import jnlp.sample.util.VersionID; public class ResourceCatalog { public static final String VERSION_XML_FILENAME = "version.xml"; private Logger _log = null; private ServletContext _servletContext = null; private HashMap _entries; /** Class to contain the information we know * about a specific directory */ static private class PathEntries { /* Version-based entries at this particular path */ private List _versionXmlList; private List _directoryList; private List _platformList; /* Last time this entry was updated */ private long _lastModified; // Last modified time of entry; public PathEntries(List versionXmlList, List directoryList, List platformList, long lastModified) { _versionXmlList = versionXmlList; _directoryList = directoryList; _platformList = platformList; _lastModified = lastModified; } public void setDirectoryList(List dirList) { _directoryList = dirList; } public List getVersionXmlList() { return _versionXmlList; } public List getDirectoryList() { return _directoryList; } public List getPlatformList() { return _platformList; } public long getLastModified() { return _lastModified; } } public ResourceCatalog(ServletContext servletContext, Logger log) { _entries = new HashMap(); _servletContext = servletContext; _log = log; } public JnlpResource lookupResource(DownloadRequest dreq) throws ErrorResponseException { // Split request up into path and name String path = dreq.getPath(); String name = null; String dir = null; int idx = path.lastIndexOf('/'); if (idx == -1) { name = path; } else { name = path.substring(idx + 1); // Exclude '/' dir = path.substring(0, idx + 1); // Include '/' } // Lookup up already parsed entries, and san directory for entries if neccesary PathEntries pentries = (PathEntries)_entries.get(dir); JnlpResource xmlVersionResPath = new JnlpResource(_servletContext, dir + VERSION_XML_FILENAME); if (pentries == null || (xmlVersionResPath.exists() && xmlVersionResPath.getLastModified() > pentries.getLastModified())) { _log.addInformational("servlet.log.scandir", dir); List dirList = scanDirectory(dir, dreq); // Scan XML file List versionList = new ArrayList(); List platformList = new ArrayList(); parseVersionXML(versionList, platformList, dir, xmlVersionResPath); pentries = new PathEntries(versionList, dirList, platformList, xmlVersionResPath.getLastModified()); _entries.put(dir, pentries); } // Search for a match JnlpResource[] result = new JnlpResource[1]; if (dreq.isPlatformRequest()) { int sts = findMatch(pentries.getPlatformList(), name, dreq, result); if (sts != DownloadResponse.STS_00_OK) { throw new ErrorResponseException(DownloadResponse.getJnlpErrorResponse(sts)); } } else { // First lookup in versions.xml file int sts1 = findMatch(pentries.getVersionXmlList(), name, dreq, result); if (sts1 != DownloadResponse.STS_00_OK) { // Then lookup in directory int sts2 = findMatch(pentries.getDirectoryList(), name, dreq, result); if (sts2 != DownloadResponse.STS_00_OK) { // fix for 4450104 // try rescan and see if it helps pentries.setDirectoryList(scanDirectory(dir, dreq)); sts2 = findMatch(pentries.getDirectoryList(), name, dreq, result); // try again after rescanning directory if (sts2 != DownloadResponse.STS_00_OK) { // Throw the most specific error code throw new ErrorResponseException(DownloadResponse.getJnlpErrorResponse(Math.max(sts1, sts2))); } } } } return result[0]; } /** This method finds the best match, or return the best error code. The * result parameter must be an array with room for one element. * * If a match is found, the method returns DownloadResponse.STS_00_OK * If one or more entries matches on: name, version-id, os, arch, and locale, * then the one with the highest version-id is set in the result[0] field. * * If a match is not found, it returns an error code, either: ERR_10_NO_RESOURCE, * ERR_11_NO_VERSION, ERR_20_UNSUP_OS, ERR_21_UNSUP_ARCH, ERR_22_UNSUP_LOCALE, * ERR_23_UNSUP_JRE. * */ public int findMatch(List list, String name, DownloadRequest dreq, JnlpResource[] result) { if (list == null) return DownloadResponse.ERR_10_NO_RESOURCE; // Setup return values VersionID bestVersionId = null; int error = DownloadResponse.ERR_10_NO_RESOURCE; VersionString vs = new VersionString(dreq.getVersion()); // Iterate through entries for(int i = 0; i < list.size(); i++) { JnlpResource respath = (JnlpResource)list.get(i); VersionID vid = new VersionID(respath.getVersionId()); int sts = matchEntry(name, vs, dreq, respath, vid); if (sts == DownloadResponse.STS_00_OK) { if (result[0] == null || vid.isGreaterThan(bestVersionId)) { result[0] = respath; bestVersionId = vid; } } else { error = Math.max(error, sts); } } return (result[0] != null) ? DownloadResponse.STS_00_OK : error; } public int matchEntry(String name, VersionString vs, DownloadRequest dreq, JnlpResource jnlpres, VersionID vid) { if (!name.equals(jnlpres.getName())) { return DownloadResponse.ERR_10_NO_RESOURCE; } if (!vs.contains(vid)) { return DownloadResponse.ERR_11_NO_VERSION; } if (!prefixMatchLists(jnlpres.getOSList(), dreq.getOS())) { return DownloadResponse.ERR_20_UNSUP_OS; } if (!prefixMatchLists(jnlpres.getArchList(), dreq.getArch())) { return DownloadResponse.ERR_21_UNSUP_ARCH; } if (!prefixMatchLists(jnlpres.getLocaleList(), dreq.getLocale())) { return DownloadResponse.ERR_22_UNSUP_LOCALE; } return DownloadResponse.STS_00_OK; } private static boolean prefixMatchStringList(String[] prefixList, String target) { // No prefixes matches everything if (prefixList == null) return true; // No target, but a prefix list does not match anything if (target == null) return false; for(int i = 0; i < prefixList.length; i++) { if (target.startsWith(prefixList[i])) return true; } return false; } /* Return true if at least one of the strings in 'prefixes' are a prefix * to at least one of the 'keys'. */ public boolean prefixMatchLists(String[] prefixes, String[] keys) { // The prefixes are part of the server resources. If none is given, // everything matches if (prefixes == null) return true; // If no os keyes was given, and the server resource is keyed of this, // then return false. if (keys == null) return false; // Check for a match on a key for(int i = 0; i < keys.length; i++) { if (prefixMatchStringList(prefixes, keys[i])) return true; } return false; } /** This method scans the directory pointed to by the * given path and creates a list of ResourcePath elements * that contains information about all the entries * * The version-based information is encoded in the file name * given the following format: * * entry ::= __ ( ). * options ::=

* Refer to the JNLP spec for details on how this is done. * * @version 1.13, 06/26/03 */ public class JarDiff implements JarDiffConstants { private static final int DEFAULT_READ_SIZE = 2048; private static byte[] newBytes = new byte[DEFAULT_READ_SIZE]; private static byte[] oldBytes = new byte[DEFAULT_READ_SIZE]; private static ResourceBundle _resources = null; // The JARDiff.java is the stand-along jardiff.jar tool. Thus, we do not // depend on Globals.java and other stuff here. Instead, we use an explicit // _debug flag. private static boolean _debug; public static ResourceBundle getResources() { if (_resources == null) { _resources = ResourceBundle.getBundle("jnlp/sample/jardiff/resources/strings"); } return _resources; } /** * Creates a patch from the two passed in files, writing the result * to os. */ public static void createPatch(String oldPath, String newPath, OutputStream os, boolean minimal) throws IOException{ JarFile2 oldJar = new JarFile2(oldPath); JarFile2 newJar = new JarFile2(newPath); try { Iterator entries; HashMap moved = new HashMap(); HashSet visited = new HashSet(); HashSet implicit = new HashSet(); HashSet moveSrc = new HashSet(); HashSet newEntries = new HashSet(); // FIRST PASS // Go through the entries in new jar and // determine which files are candidates for implicit moves // ( files that has the same filename and same content in old.jar // and new.jar ) // and for files that cannot be implicitly moved, we will either // find out whether it is moved or new (modified) entries = newJar.getJarEntries(); if (entries != null) { while (entries.hasNext()) { JarEntry newEntry = (JarEntry)entries.next(); String newname = newEntry.getName(); // Return best match of contents, will return a name match if possible String oldname = oldJar.getBestMatch(newJar, newEntry); if (oldname == null) { // New or modified entry if (_debug) { System.out.println("NEW: "+ newname); } newEntries.add(newname); } else { // Content already exist - need to do a move // Should do implicit move? Yes, if names are the same, and // no move command already exist from oldJar if (oldname.equals(newname) && !moveSrc.contains(oldname)) { if (_debug) { System.out.println(newname + " added to implicit set!"); } implicit.add(newname); } else { // The 1.0.1/1.0 JarDiffPatcher cannot handle // multiple MOVE command with same src. // The work around here is if we are going to generate // a MOVE command with duplicate src, we will // instead add the target as a new file. This way // the jardiff can be applied by 1.0.1/1.0 // JarDiffPatcher also. if (!minimal && (implicit.contains(oldname) || moveSrc.contains(oldname) )) { // generate non-minimal jardiff // for backward compatibility if (_debug) { System.out.println("NEW: "+ newname); } newEntries.add(newname); } else { // Use newname as key, since they are unique if (_debug) { System.err.println("moved.put " + newname + " " + oldname); } moved.put(newname, oldname); moveSrc.add(oldname); } // Check if this disables an implicit 'move ' if (implicit.contains(oldname) && minimal) { if (_debug) { System.err.println("implicit.remove " + oldname); System.err.println("moved.put " + oldname + " " + oldname); } implicit.remove(oldname); moved.put(oldname, oldname); moveSrc.add(oldname); } } } } } //if (entries != null) // SECOND PASS: = - - // - ArrayList deleted = new ArrayList(); entries = oldJar.getJarEntries(); if (entries != null) { while (entries.hasNext()) { JarEntry oldEntry = (JarEntry)entries.next(); String oldName = oldEntry.getName(); if (!implicit.contains(oldName) && !moveSrc.contains(oldName) && !newEntries.contains(oldName)) { if (_debug) { System.err.println("deleted.add " + oldName); } deleted.add(oldName); } } } //DEBUG if (_debug) { //DEBUG: print out moved map entries = moved.keySet().iterator(); if (entries != null) { System.out.println("MOVED MAP!!!"); while (entries.hasNext()) { String newName = (String)entries.next(); String oldName = (String)moved.get(newName); System.out.println("key is " + newName + " value is " + oldName); } } //DEBUG: print out IMOVE map entries = implicit.iterator(); if (entries != null) { System.out.println("IMOVE MAP!!!"); while (entries.hasNext()) { String newName = (String)entries.next(); System.out.println("key is " + newName); } } } JarOutputStream jos = new JarOutputStream(os); // Write out all the MOVEs and REMOVEs createIndex(jos, deleted, moved); // Put in New and Modified entries entries = newEntries.iterator(); if (entries != null) { while (entries.hasNext()) { String newName = (String)entries.next(); if (_debug) { System.out.println("New File: " + newName); } writeEntry(jos, newJar.getEntryByName(newName), newJar); } } jos.finish(); jos.close(); } catch (IOException ioE){ throw ioE; } finally { try { oldJar.getJarFile().close(); } catch (IOException e1) { //ignore } try { newJar.getJarFile().close(); } catch (IOException e1) { //ignore } } // finally } /** * Writes the index file out to jos. * oldEntries gives the names of the files that were removed, * movedMap maps from the new name to the old name. */ private static void createIndex(JarOutputStream jos, List oldEntries, Map movedMap) throws IOException { StringWriter writer = new StringWriter(); writer.write(VERSION_HEADER); writer.write("\r\n"); // Write out entries that have been removed for (int counter = 0; counter < oldEntries.size(); counter++) { String name = (String)oldEntries.get(counter); writer.write(REMOVE_COMMAND); writer.write(" "); writeEscapedString(writer, name); writer.write("\r\n"); } // And those that have moved Iterator names = movedMap.keySet().iterator(); if (names != null) { while (names.hasNext()) { String newName = (String)names.next(); String oldName = (String)movedMap.get(newName); writer.write(MOVE_COMMAND); writer.write(" "); writeEscapedString(writer, oldName); writer.write(" "); writeEscapedString(writer, newName); writer.write("\r\n"); } } JarEntry je = new JarEntry(INDEX_NAME); byte[] bytes = writer.toString().getBytes("UTF-8"); writer.close(); jos.putNextEntry(je); jos.write(bytes, 0, bytes.length); } private static void writeEscapedString(Writer writer, String string) throws IOException { int index = 0; int last = 0; char[] chars = null; while ((index = string.indexOf(' ', index)) != -1) { if (last != index) { if (chars == null) { chars = string.toCharArray(); } writer.write(chars, last, index - last); } last = index; index++; writer.write('\\'); } if (last != 0) { writer.write(chars, last, chars.length - last); } else { // no spaces writer.write(string); } } private static void writeEntry(JarOutputStream jos, JarEntry entry, JarFile2 file) throws IOException { writeEntry(jos, entry, file.getJarFile().getInputStream(entry)); } private static void writeEntry(JarOutputStream jos, JarEntry entry, InputStream data) throws IOException { jos.putNextEntry(entry); try { // Read the entry int size = data.read(newBytes); while (size != -1) { jos.write(newBytes, 0, size); size = data.read(newBytes); } } catch(IOException ioE) { throw ioE; } finally { try { data.close(); } catch(IOException e){ //Ignore } } } /** * JarFile2 wraps a JarFile providing some convenience methods. */ private static class JarFile2 { private JarFile _jar; private List _entries; private HashMap _nameToEntryMap; private HashMap _crcToEntryMap; public JarFile2(String path) throws IOException { _jar = new JarFile(new File(path)); index(); } public JarFile getJarFile() { return _jar; } public Iterator getJarEntries() { return _entries.iterator(); } public JarEntry getEntryByName(String name) { return (JarEntry)_nameToEntryMap.get(name); } /** * Returns true if the two InputStreams differ. */ private static boolean differs(InputStream oldIS, InputStream newIS) throws IOException { int newSize = 0; int oldSize; int total = 0; boolean retVal = false; try{ while (newSize != -1) { newSize = newIS.read(newBytes); oldSize = oldIS.read(oldBytes); if (newSize != oldSize) { if (_debug) { System.out.println("\tread sizes differ: " + newSize + " " + oldSize + " total " + total); } retVal = true; break; } if (newSize > 0) { while (--newSize >= 0) { total++; if (newBytes[newSize] != oldBytes[newSize]) { if (_debug) { System.out.println("\tbytes differ at " + total); } retVal = true; break; } if ( retVal ) { //Jump out break; } newSize = 0; } } } } catch(IOException ioE){ throw ioE; } finally { try { oldIS.close(); } catch(IOException e){ //Ignore } try { newIS.close(); } catch(IOException e){ //Ignore } } return retVal; } public String getBestMatch(JarFile2 file, JarEntry entry) throws IOException { // check for same name and same content, return name if found if (contains(file, entry)) { return (entry.getName()); } // return name of same content file or null return (hasSameContent(file,entry)); } public boolean contains(JarFile2 f, JarEntry e) throws IOException { JarEntry thisEntry = getEntryByName(e.getName()); // Look up name in 'this' Jar2File - if not exist return false if (thisEntry == null) return false; // Check CRC - if no match - return false if (thisEntry.getCrc() != e.getCrc()) return false; // Check contents - if no match - return false InputStream oldIS = getJarFile().getInputStream(thisEntry); InputStream newIS = f.getJarFile().getInputStream(e); boolean retValue = differs(oldIS, newIS); return !retValue; } public String hasSameContent(JarFile2 file, JarEntry entry) throws IOException { String thisName = null; Long crcL = new Long(entry.getCrc()); // check if this jar contains files with the passed in entry's crc if (_crcToEntryMap.containsKey(crcL)) { // get the Linked List with files with the crc LinkedList ll = (LinkedList)_crcToEntryMap.get(crcL); // go through the list and check for content match ListIterator li = ll.listIterator(0); if (li != null) { while (li.hasNext()) { JarEntry thisEntry = (JarEntry)li.next(); // check for content match InputStream oldIS = getJarFile().getInputStream(thisEntry); InputStream newIS = file.getJarFile().getInputStream(entry); if (!differs(oldIS, newIS)) { thisName = thisEntry.getName(); return thisName; } } } } return thisName; } private void index() throws IOException { Enumeration entries = _jar.entries(); _nameToEntryMap = new HashMap(); _crcToEntryMap = new HashMap(); _entries = new ArrayList(); if (_debug) { System.out.println("indexing: " + _jar.getName()); } if (entries != null) { while (entries.hasMoreElements()) { JarEntry entry = (JarEntry)entries.nextElement(); long crc = entry.getCrc(); Long crcL = new Long(crc); if (_debug) { System.out.println("\t" + entry.getName() + " CRC " + crc); } _nameToEntryMap.put(entry.getName(), entry); _entries.add(entry); // generate the CRC to entries map if (_crcToEntryMap.containsKey(crcL)) { // key exist, add the entry to the correcponding // linked list // get the linked list LinkedList ll = (LinkedList)_crcToEntryMap.get(crcL); // put in the new entry ll.add(entry); // put it back in the hash map _crcToEntryMap.put(crcL, ll); } else { // create a new entry in the hashmap for the new key // first create the linked list and put in the new // entry LinkedList ll = new LinkedList(); ll.add(entry); // create the new entry in the hashmap _crcToEntryMap.put(crcL, ll); } } } } } private static void showHelp() { System.out.println("JarDiff: [-nonminimal (for backward compatibility with 1.0.1/1.0] [-creatediff | -applydiff] [-output file] old.jar new.jar"); } // -creatediff -applydiff -debug -output file public static void main(String[] args) throws IOException { boolean diff = true; boolean minimal = true; String outputFile = "out.jardiff"; for (int counter = 0; counter < args.length; counter++) { // for backward compatibilty with 1.0.1/1.0 if (args[counter].equals("-nonminimal") || args[counter].equals("-n")) { minimal = false; } else if (args[counter].equals("-creatediff") || args[counter].equals("-c")) { diff = true; } else if (args[counter].equals("-applydiff") || args[counter].equals("-a")) { diff = false; } else if (args[counter].equals("-debug") || args[counter].equals("-d")) { _debug = true; } else if (args[counter].equals("-output") || args[counter].equals("-o")) { if (++counter < args.length) { outputFile = args[counter]; } } else if (args[counter].equals("-applydiff") || args[counter].equals("-a")) { diff = false; } else { if ((counter + 2) != args.length) { showHelp(); System.exit(0); } if (diff) { try { OutputStream os = new FileOutputStream(outputFile); JarDiff.createPatch(args[counter], args[counter + 1], os, minimal); os.close(); } catch (IOException ioe) { try { System.out.println(getResources().getString("jardiff.error.create") + " " + ioe); } catch (MissingResourceException mre) { } } } else { try { OutputStream os = new FileOutputStream(outputFile); new JarDiffPatcher().applyPatch( null, args[counter], args[counter + 1], os); os.close(); } catch (IOException ioe) { try { System.out.println(getResources().getString("jardiff.error.apply") + " " + ioe); } catch (MissingResourceException mre) { } } } System.exit(0); } } showHelp(); } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/jardiff/JarDiffConstants.javajnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/jardiff/JarDiffConstan0000664000175000017500000000437411710312010031104 0ustar andrewandrew/* * @(#)JarDiffConstants.java 1.7 10/01/12 * * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ package jnlp.sample.jardiff; import java.io.*; import java.util.*; import java.util.jar.*; import java.util.zip.*; /** * Constants used by creating patch and applying patch for JarDiff. * * @version 1.8, 06/26/03 */ public interface JarDiffConstants { public final String VERSION_HEADER = "version 1.0"; public final String INDEX_NAME = "META-INF/INDEX.JD"; public final String REMOVE_COMMAND = "remove"; public final String MOVE_COMMAND = "move"; } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/jardiff/JarDiffPatcher.javajnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/jardiff/JarDiffPatcher0000664000175000017500000002622711710312010031066 0ustar andrewandrew/* * @(#)JarDiffPatcher.java 1.8 10/01/12 * * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ package jnlp.sample.jardiff; import java.io.*; import java.util.*; import java.net.URL; import java.util.jar.*; import java.util.zip.*; /** * JarDiff is able to create a jar file containing the delta between two * jar files (old and new). The delta jar file can then be applied to the * old jar file to reconstruct the new jar file. *

* Refer to the JNLP spec for details on how this is done. * * @version 1.11, 06/26/03 */ public class JarDiffPatcher implements JarDiffConstants, Patcher { private static final int DEFAULT_READ_SIZE = 2048; private static byte[] newBytes = new byte[DEFAULT_READ_SIZE]; private static byte[] oldBytes = new byte[DEFAULT_READ_SIZE]; private static ResourceBundle _resources = JarDiff.getResources(); public static ResourceBundle getResources() { return JarDiff.getResources(); } public void applyPatch(Patcher.PatchDelegate delegate, String oldJarPath, String jarDiffPath, OutputStream result) throws IOException { File oldFile = new File(oldJarPath); File diffFile = new File(jarDiffPath); JarOutputStream jos = new JarOutputStream(result); JarFile oldJar = new JarFile(oldFile); JarFile jarDiff = new JarFile(diffFile); Set ignoreSet = new HashSet(); Map renameMap = new HashMap(); determineNameMapping(jarDiff, ignoreSet, renameMap); // get all keys in renameMap Object[] keys = renameMap.keySet().toArray(); // Files to implicit move Set oldjarNames = new HashSet(); Enumeration oldEntries = oldJar.entries(); if (oldEntries != null) { while (oldEntries.hasMoreElements()) { oldjarNames.add(((JarEntry)oldEntries.nextElement()).getName()); } } // size depends on the three parameters below, which is // basically the counter for each loop that do the actual // writes to the output file // since oldjarNames.size() changes in the first two loop // below, we need to adjust the size accordingly also when // oldjarNames.size() changes double size = oldjarNames.size() + keys.length + jarDiff.size(); double currentEntry = 0; // Handle all remove commands oldjarNames.removeAll(ignoreSet); size -= ignoreSet.size(); // Add content from JARDiff Enumeration entries = jarDiff.entries(); if (entries != null) { while (entries.hasMoreElements()) { JarEntry entry = (JarEntry)entries.nextElement(); if (!INDEX_NAME.equals(entry.getName())) { updateDelegate(delegate, currentEntry, size); currentEntry++; writeEntry(jos, entry, jarDiff); // Remove entry from oldjarNames since no implicit //move is needed boolean wasInOld = oldjarNames.remove(entry.getName()); // Update progress counters. If it was in old, we do // not need an implicit move, so adjust total size. if (wasInOld) size--; } else { // no write is done, decrement size size--; } } } // go through the renameMap and apply move for each entry for (int j = 0; j < keys.length; j++) { // Apply move command String newName = (String)keys[j]; String oldName = (String)renameMap.get(newName); // Get source JarEntry JarEntry oldEntry = oldJar.getJarEntry(oldName); if (oldEntry == null) { String moveCmd = MOVE_COMMAND + oldName + " " + newName; handleException("jardiff.error.badmove", moveCmd); } // Create dest JarEntry JarEntry newEntry = new JarEntry(newName); newEntry.setTime(oldEntry.getTime()); newEntry.setSize(oldEntry.getSize()); newEntry.setCompressedSize(oldEntry.getCompressedSize()); newEntry.setCrc(oldEntry.getCrc()); newEntry.setMethod(oldEntry.getMethod()); newEntry.setExtra(oldEntry.getExtra()); newEntry.setComment(oldEntry.getComment()); updateDelegate(delegate, currentEntry, size); currentEntry++; writeEntry(jos, newEntry, oldJar.getInputStream(oldEntry)); // Remove entry from oldjarNames since no implicit //move is needed boolean wasInOld = oldjarNames.remove(oldName); // Update progress counters. If it was in old, we do // not need an implicit move, so adjust total size. if (wasInOld) size--; } // implicit move Iterator iEntries = oldjarNames.iterator(); if (iEntries != null) { while (iEntries.hasNext()) { String name = (String)iEntries.next(); JarEntry entry = oldJar.getJarEntry(name); updateDelegate(delegate, currentEntry, size); currentEntry++; writeEntry(jos, entry, oldJar); } } updateDelegate(delegate, currentEntry, size); jos.finish(); } private void updateDelegate(Patcher.PatchDelegate delegate, double currentSize, double size) { if (delegate != null) { delegate.patching((int)(currentSize/size)); } } private void determineNameMapping(JarFile jarDiff, Set ignoreSet, Map renameMap) throws IOException { InputStream is = jarDiff.getInputStream(jarDiff.getEntry(INDEX_NAME)); if (is == null) { handleException("jardiff.error.noindex", null); } LineNumberReader indexReader = new LineNumberReader (new InputStreamReader(is, "UTF-8")); String line = indexReader.readLine(); if (line == null || !line.equals(VERSION_HEADER)) { handleException("jardiff.error.badheader", line); } while ((line = indexReader.readLine()) != null) { if (line.startsWith(REMOVE_COMMAND)) { List sub = getSubpaths(line.substring(REMOVE_COMMAND. length())); if (sub.size() != 1) { handleException("jardiff.error.badremove", line); } ignoreSet.add(sub.get(0)); } else if (line.startsWith(MOVE_COMMAND)) { List sub = getSubpaths(line.substring(MOVE_COMMAND.length())); if (sub.size() != 2) { handleException("jardiff.error.badmove", line); } // target of move should be the key if (renameMap.put(sub.get(1), sub.get(0)) != null) { // invalid move - should not move to same target twice handleException("jardiff.error.badmove", line); } } else if (line.length() > 0) { handleException("jardiff.error.badcommand", line); } } } private void handleException(String errorMsg, String line) throws IOException { try { throw new IOException(getResources().getString(errorMsg) + " " + line); } catch (MissingResourceException mre) { System.err.println("Fatal error: " + errorMsg); new Throwable().printStackTrace(System.err); System.exit(-1); } } private List getSubpaths(String path) { int index = 0; int length = path.length(); ArrayList sub = new ArrayList(); while (index < length) { while (index < length && Character.isWhitespace (path.charAt(index))) { index++; } if (index < length) { int start = index; int last = start; String subString = null; while (index < length) { char aChar = path.charAt(index); if (aChar == '\\' && (index + 1) < length && path.charAt(index + 1) == ' ') { if (subString == null) { subString = path.substring(last, index); } else { subString += path.substring(last, index); } last = ++index; } else if (Character.isWhitespace(aChar)) { break; } index++; } if (last != index) { if (subString == null) { subString = path.substring(last, index); } else { subString += path.substring(last, index); } } sub.add(subString); } } return sub; } private void writeEntry(JarOutputStream jos, JarEntry entry, JarFile file) throws IOException { writeEntry(jos, entry, file.getInputStream(entry)); } private void writeEntry(JarOutputStream jos, JarEntry entry, InputStream data) throws IOException { //Create a new ZipEntry to clear the compressed size. 5079423 jos.putNextEntry(new ZipEntry(entry.getName())); // Read the entry int size = data.read(newBytes); while (size != -1) { jos.write(newBytes, 0, size); size = data.read(newBytes); } data.close(); } } jnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/util/0000775000175000017500000000000011710312010025666 5ustar andrewandrew././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/util/VersionString.javajnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/util/VersionString.jav0000664000175000017500000000770711710312010031217 0ustar andrewandrew/* * @(#)VersionString.java 1.7 10/01/12 * * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ package jnlp.sample.util; import java.util.ArrayList; import java.util.StringTokenizer; /* * Utility class that knows to handle version strings * A version string is of the form: * * (version-id ('+'?) ' ') * * */ public class VersionString { private ArrayList _versionIds; /** Constructs a VersionString object from string */ public VersionString(String vs) { _versionIds = new ArrayList(); if (vs != null) { StringTokenizer st = new StringTokenizer(vs, " ", false); while(st.hasMoreElements()) { // Note: The VersionID class takes care of a postfixed '+' _versionIds.add(new VersionID(st.nextToken())); } } } /** Check if this VersionString object contains the VersionID m */ public boolean contains(VersionID m) { for(int i = 0; i < _versionIds.size(); i++) { VersionID vi = (VersionID)_versionIds.get(i); boolean check = vi.match(m); if (check) return true; } return false; } /** Check if this VersionString object contains the VersionID m, given as a string */ public boolean contains(String versionid) { return contains(new VersionID(versionid)); } /** Check if this VersionString object contains anything greater than m */ public boolean containsGreaterThan(VersionID m) { for(int i = 0; i < _versionIds.size(); i++) { VersionID vi = (VersionID)_versionIds.get(i); boolean check = vi.isGreaterThan(m); if (check) return true; } return false; } /** Check if this VersionString object contains anything greater than the VersionID m, given as a string */ public boolean containsGreaterThan(String versionid) { return containsGreaterThan(new VersionID(versionid)); } /** Check if the versionString 'vs' contains the VersionID 'vi' */ static public boolean contains(String vs, String vi) { return (new VersionString(vs)).contains(vi); } /** Pretty-print object */ public String toString() { StringBuffer sb = new StringBuffer(); for(int i = 0; i < _versionIds.size(); i++) { sb.append(_versionIds.get(i).toString()); sb.append(' '); } return sb.toString(); } } jnlp-servlet-527f95ceaf5b0d746348b31c647b93900e72258a/src/classes/jnlp/sample/util/VersionID.java0000664000175000017500000002001311710312010030367 0ustar andrewandrew/* * @(#)VersionID.java 1.8 10/01/12 * * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ package jnlp.sample.util; import java.util.ArrayList; import java.util.Arrays; /** * VersionID contains a JNLP version ID. * * The VersionID also contains a prefix indicator that can * be used when stored with a VersionString * */ public class VersionID implements Comparable { private String[] _tuple; // Array of Integer or String objects private boolean _usePrefixMatch; // star (*) prefix private boolean _useGreaterThan; // plus (+) greather-than private boolean _isCompound; // and (&) operator private VersionID _rest; // remaining part after the & /** Creates a VersionID object */ public VersionID(String str) { _usePrefixMatch = false; _useGreaterThan = false; _isCompound = false; if (str == null && str.length() == 0) { _tuple = new String[0]; return; } // Check for compound int amp = str.indexOf("&"); if (amp >= 0) { _isCompound = true; VersionID firstPart = new VersionID(str.substring(0, amp)); _rest = new VersionID(str.substring(amp+1)); _tuple = firstPart._tuple; _usePrefixMatch = firstPart._usePrefixMatch; _useGreaterThan = firstPart._useGreaterThan; } else { // Check for postfix if (str.endsWith("+")) { _useGreaterThan = true; str = str.substring(0, str.length() - 1); } else if (str.endsWith("*")) { _usePrefixMatch = true; str = str.substring(0, str.length() - 1); } ArrayList list = new ArrayList(); int start = 0; for(int i = 0; i < str.length(); i++) { // Split at each separator character if (".-_".indexOf(str.charAt(i)) != -1) { if (start < i) { String value = str.substring(start, i); list.add(value); } start = i + 1; } } if (start < str.length()) { list.add(str.substring(start, str.length())); } _tuple = new String[list.size()]; _tuple = (String[])list.toArray(_tuple); } } /** Returns true if no flags are set */ public boolean isSimpleVersion() { return !_useGreaterThan && !_usePrefixMatch && !_isCompound; } /** Match 'this' versionID against vid. * The _usePrefixMatch/_useGreaterThan flag is used to determine if a * prefix match of an exact match should be performed * if _isCompound, must match _rest also. */ public boolean match(VersionID vid) { if (_isCompound) { if (!_rest.match(vid)) { return false; } } return (_usePrefixMatch) ? this.isPrefixMatch(vid) : (_useGreaterThan) ? vid.isGreaterThanOrEqual(this) : matchTuple(vid); } /** Compares if two version IDs are equal */ public boolean equals(Object o) { if (matchTuple(o)) { VersionID ov = (VersionID) o; if (_rest == null || _rest.equals(ov._rest)) { if ((_useGreaterThan == ov._useGreaterThan) && (_usePrefixMatch == ov._usePrefixMatch)) { return true; } } } return false; } /** Compares if two version IDs are equal */ private boolean matchTuple(Object o) { // Check for null and type if (o == null || !(o instanceof VersionID)) return false; VersionID vid = (VersionID)o; // Normalize arrays String[] t1 = normalize(_tuple, vid._tuple.length); String[] t2 = normalize(vid._tuple, _tuple.length); // Check contents for(int i = 0; i < t1.length; i++) { Object o1 = getValueAsObject(t1[i]); Object o2 = getValueAsObject(t2[i]); if (!o1.equals(o2)) return false; } return true; } private Object getValueAsObject(String value) { if (value.length() > 0 && value.charAt(0) != '-') { try { return Integer.valueOf(value); } catch(NumberFormatException nfe) { /* fall through */ } } return value; } public boolean isGreaterThan(VersionID vid) { return isGreaterThanOrEqualHelper(vid, false); } public boolean isGreaterThanOrEqual(VersionID vid) { return isGreaterThanOrEqualHelper(vid, true); } /** Compares if 'this' is greater than vid */ private boolean isGreaterThanOrEqualHelper(VersionID vid, boolean allowEqual) { if (_isCompound) { if (!_rest.isGreaterThanOrEqualHelper(vid, allowEqual)) { return false; } } // Normalize the two strings String[] t1 = normalize(_tuple, vid._tuple.length); String[] t2 = normalize(vid._tuple, _tuple.length); for(int i = 0; i < t1.length; i++) { // Compare current element Object e1 = getValueAsObject(t1[i]); Object e2 = getValueAsObject(t2[i]); if (e1.equals(e2)) { // So far so good } else { if (e1 instanceof Integer && e2 instanceof Integer) { return ((Integer)e1).intValue() > ((Integer)e2).intValue(); } else { String s1 = t1[i].toString(); String s2 = t2[i].toString(); return s1.compareTo(s2) > 0; } } } // If we get here, they are equal return allowEqual; } /** Checks if 'this' is a prefix of vid */ public boolean isPrefixMatch(VersionID vid) { if (_isCompound) { if (!_rest.isPrefixMatch(vid)) { return false; } } // Make sure that vid is at least as long as the prefix String[] t2 = normalize(vid._tuple, _tuple.length); for(int i = 0; i < _tuple.length; i++) { Object e1 = _tuple[i]; Object e2 = t2[i]; if (e1.equals(e2)) { // So far so good } else { // Not a prefix return false; } } return true; } /** Normalize an array to a certain lengh */ private String[] normalize(String[] list, int minlength) { if (list.length < minlength) { // Need to do padding String[] newlist = new String[minlength]; System.arraycopy(list, 0, newlist, 0, list.length); Arrays.fill(newlist, list.length, newlist.length, "0"); return newlist; } else { return list; } } public int compareTo(Object o) { if (o == null || !(o instanceof VersionID)) return -1; VersionID vid = (VersionID)o; return equals(vid) ? 0 : (isGreaterThanOrEqual(vid) ? 1 : -1); } /** Show it as a string */ public String toString() { StringBuffer sb = new StringBuffer(); for(int i = 0; i < _tuple.length -1; i++) { sb.append(_tuple[i]); sb.append('.'); } if (_tuple.length > 0 ) sb.append(_tuple[_tuple.length - 1]); if (_usePrefixMatch) sb.append('+'); return sb.toString(); } }