changeset 1511:1b6706eb48a3

Added optional windows desktop integration * AUTHORS: added Joel * Makefile: Excluded (WindowsDesktopEntry.java) if mslinks are not included, added mslinks to UNIFIED_CLASSPATH_SEGMENTS included mslinks to windows and linux runtime libs, added MSLINKS_JAR to other composeclasspath calls * NEWS: mentioned windows desktop support, mentioned listing of cache and operations via id. * acinclude.m4: added check (IT_CHECK_FOR_MSLINKS) for optional mslinks.jar, strong warning printed if build is on windows * configure.ac: call (IT_CHECK_FOR_MSLINKS) * netx/net/sourceforge/jnlp/Launcher.java: new variable of (KEY_JAVAWS_LOCATION) to replace hardcoded icedtea-web.bin.location over netx. * netx/net/sourceforge/jnlp/OptionsDefinitions.java: re-declared clear cache to take none or one argument. Added Xcacheids switch for listing the cache (works with verbose) * netx/net/sourceforge/jnlp/cache/CacheDirectory.java: refactored hardcoded ".info" to constant. * netx/net/sourceforge/jnlp/cache/CacheEntry.java: introduced KEY_JNLP_PATH and used to set jnlp-path attribute * netx/net/sourceforge/jnlp/cache/CacheLRUWrapper.java: hide private constructor, declared and provided (windowsShortcutList) * netx/net/sourceforge/jnlp/cache/CacheUtil.java: extracted and used (checkToClearCache). Added second method clearCache with arg to clear only specific part of cache. Clear cache also alerts windows desktop files now via new removeWindowsShortcuts. Added methods to lists ids and details from cache listCacheIds and getCacheIds. Included new inner class CacheId to encapsualte various types of id - CacheJnlpId and CacheDomainId now. * netx/net/sourceforge/jnlp/cache/DirectoryNode.java: only adapted to .info refactoring * netx/net/sourceforge/jnlp/cache/ResourceDownloader.java: Save main argument, or jnlp argument or html argument to jnlp-path .info entry if found. * netx/net/sourceforge/jnlp/controlpanel/CacheAppViewer.java: gui to itweb-settings cache pane to allow comfortable listing of ids and deleting via those grouping. New file. * netx/net/sourceforge/jnlp/controlpanel/CachePane.java: added logic to show .info details for each file shown by cache viewer. (generateData) made jnlp-path aware, made public and reused several times * netx/net/sourceforge/jnlp/controlpanel/TemporaryInternetFilesPanel.java: added button to show dilog which is deleting by id * netx/net/sourceforge/jnlp/resources/Messages.properties: added BXclearcache BXSingleCacheCleared BXSingleCacheClearNotFound BXSingleCacheMoreThenOneId BXSingleCacheFileCount BXcacheids NOAnonorone WinDesktopError. Modified BXclearcache. Improved EXAWdesktopWants EXAWdesktopDontWants EXAWsubmenu EXAWmenuWants EXAWmenuDontWants EXAWrememberByApp EXAWrememberByPage EXAWrememberByAppTooltip EXAWbrowsersTolltip SDesktopShortcut * netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java: added if isWindows reflective calls to WindowsDesktopEntry and original XdesktopEntry work moved to else part. * netx/net/sourceforge/jnlp/runtime/Boot.java: now offer getter for optionParser (so it van be reused in ResourceDownloader) Added understanding to Xcacheids. Understanding to Xclearcache adapted to its new optional argument * netx/net/sourceforge/jnlp/util/GenericDesktopEntry.java: interface for (future) WindowsDesktopEntry and XDesktopEntry unification * netx/net/sourceforge/jnlp/util/WindowsDesktopEntry.java: new file, implementation fo windos desktop integration via lnk files generated by mslinks.jar. Unlike XDesktop integration, it swarms also uninstall shortcuts. * netx/net/sourceforge/jnlp/util/XDesktopEntry.java: Mostly adapted to refactorings. Extracted extraction of favicon to method, reused, and improved to try more locations on server. * netx/net/sourceforge/jnlp/util/optionparser/OptionParser.java: adapted to refactorings * launcher/launchers.bat.in: mslinks included in bootclasspath * tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java: addd tests for CacheId * tests/netx/unit/net/sourceforge/jnlp/util/XDesktopEntryTest.java: Added tests for favicon extracti
author Jiri Vanek <jvanek@redhat.com>
date Mon, 25 Feb 2019 13:41:58 +0100
parents 3a998e74a0f9
children 324019b81bb1
files AUTHORS ChangeLog Makefile.am NEWS acinclude.m4 configure.ac launcher/launchers.bat.in netx/net/sourceforge/jnlp/Launcher.java netx/net/sourceforge/jnlp/OptionsDefinitions.java netx/net/sourceforge/jnlp/cache/CacheDirectory.java netx/net/sourceforge/jnlp/cache/CacheEntry.java netx/net/sourceforge/jnlp/cache/CacheLRUWrapper.java netx/net/sourceforge/jnlp/cache/CacheUtil.java netx/net/sourceforge/jnlp/cache/DirectoryNode.java netx/net/sourceforge/jnlp/cache/ResourceDownloader.java netx/net/sourceforge/jnlp/controlpanel/CacheAppViewer.java netx/net/sourceforge/jnlp/controlpanel/CachePane.java netx/net/sourceforge/jnlp/controlpanel/TemporaryInternetFilesPanel.java netx/net/sourceforge/jnlp/resources/Messages.properties netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java netx/net/sourceforge/jnlp/runtime/Boot.java netx/net/sourceforge/jnlp/util/GenericDesktopEntry.java netx/net/sourceforge/jnlp/util/WindowsDesktopEntry.java netx/net/sourceforge/jnlp/util/XDesktopEntry.java netx/net/sourceforge/jnlp/util/optionparser/OptionParser.java tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java tests/netx/unit/net/sourceforge/jnlp/util/XDesktopEntryTest.java
diffstat 27 files changed, 1287 insertions(+), 135 deletions(-) [+]
line wrap: on
line diff
--- a/AUTHORS	Wed Jan 09 17:41:55 2019 +0100
+++ b/AUTHORS	Mon Feb 25 13:41:58 2019 +0100
@@ -38,6 +38,7 @@
 Fridrich Strba <fridrich.strba@suse.com>
 Andrew Su <asu@redhat.com>
 Joshua Sumali <jsumali@redhat.com>
+Joel Tesdall <jtesdall@mapcon.com>
 Michal Vala <mvala@redhat.com>
 Jiri Vanek <jvanek@redhat.com>
 Tomáš Votava <tomcacolca@gmail.com>
--- a/ChangeLog	Wed Jan 09 17:41:55 2019 +0100
+++ b/ChangeLog	Mon Feb 25 13:41:58 2019 +0100
@@ -1,3 +1,49 @@
+2018-01-07  Joel Tesdall <jtesdall@mapcon.com>
+            Jiri Vanek <jvanek@redhat.com>
+
+	Added optional windows desktop integration
+	* AUTHORS: added Joel
+	* Makefile: Excluded (WindowsDesktopEntry.java) if mslinks are not included, added mslinks to UNIFIED_CLASSPATH_SEGMENTS
+	included mslinks to windows and linux runtime libs, added MSLINKS_JAR to other composeclasspath calls
+	* NEWS: mentioned windows desktop support, mentioned listing of cache and operations via id.
+	* acinclude.m4: added check (IT_CHECK_FOR_MSLINKS) for optional mslinks.jar, strong warning printed if build is on windows
+	* configure.ac: call (IT_CHECK_FOR_MSLINKS)
+	* netx/net/sourceforge/jnlp/Launcher.java: new variable of (KEY_JAVAWS_LOCATION) to replace hardcoded icedtea-web.bin.location over netx.
+	* netx/net/sourceforge/jnlp/OptionsDefinitions.java:  re-declared clear cache to take none or one argument.
+	Added Xcacheids switch for listing the cache (works with verbose)
+	* netx/net/sourceforge/jnlp/cache/CacheDirectory.java: refactored hardcoded ".info" to constant.
+	* netx/net/sourceforge/jnlp/cache/CacheEntry.java: introduced KEY_JNLP_PATH and used to set jnlp-path attribute
+	* netx/net/sourceforge/jnlp/cache/CacheLRUWrapper.java: hide private constructor, declared and provided (windowsShortcutList)
+	* netx/net/sourceforge/jnlp/cache/CacheUtil.java: extracted and used (checkToClearCache). Added second method clearCache
+	with arg to clear only specific part of cache. Clear cache also alerts windows desktop files now via new removeWindowsShortcuts.
+	Added methods to lists ids and details from cache listCacheIds and getCacheIds. Included new inner class CacheId to encapsualte
+	various types of id - CacheJnlpId and CacheDomainId now.
+	* netx/net/sourceforge/jnlp/cache/DirectoryNode.java: only adapted to .info refactoring
+	* netx/net/sourceforge/jnlp/cache/ResourceDownloader.java: Save main argument, or jnlp argument or html argument to
+	jnlp-path .info entry if found.
+	* netx/net/sourceforge/jnlp/controlpanel/CacheAppViewer.java: gui to itweb-settings cache pane to allow comfortable
+	listing of ids and deleting via those grouping.	New file.
+	* netx/net/sourceforge/jnlp/controlpanel/CachePane.java: added logic to show .info details for each file shown by cache viewer.
+	(generateData) made jnlp-path aware, made public and reused several times
+	* netx/net/sourceforge/jnlp/controlpanel/TemporaryInternetFilesPanel.java: added button to show dilog which is deleting by id
+	* netx/net/sourceforge/jnlp/resources/Messages.properties: added BXclearcache BXSingleCacheCleared BXSingleCacheClearNotFound
+	BXSingleCacheMoreThenOneId BXSingleCacheFileCount BXcacheids NOAnonorone WinDesktopError. Modified BXclearcache. Improved
+	EXAWdesktopWants EXAWdesktopDontWants EXAWsubmenu EXAWmenuWants EXAWmenuDontWants EXAWrememberByApp EXAWrememberByPage
+	EXAWrememberByAppTooltip EXAWbrowsersTolltip SDesktopShortcut
+	* netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java: added if isWindows reflective calls to  WindowsDesktopEntry
+	and original XdesktopEntry work moved to else part.
+	* netx/net/sourceforge/jnlp/runtime/Boot.java: now offer getter for optionParser (so it van be reused in ResourceDownloader)
+	Added understanding to Xcacheids. Understanding to Xclearcache adapted to its new optional argument
+	* netx/net/sourceforge/jnlp/util/GenericDesktopEntry.java: interface for (future) WindowsDesktopEntry and XDesktopEntry unification
+	* netx/net/sourceforge/jnlp/util/WindowsDesktopEntry.java: new file, implementation fo windos desktop integration
+	via lnk files generated by mslinks.jar. Unlike XDesktop integration, it swarms also uninstall shortcuts.
+	* netx/net/sourceforge/jnlp/util/XDesktopEntry.java: Mostly adapted to refactorings. Extracted extraction of favicon to method,
+	reused, and improved to try more locations on server.
+	* netx/net/sourceforge/jnlp/util/optionparser/OptionParser.java: adapted to refactorings
+	* launcher/launchers.bat.in: mslinks included in bootclasspath
+	* tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java: addd tests for CacheId
+	* tests/netx/unit/net/sourceforge/jnlp/util/XDesktopEntryTest.java: Added tests for favicon extraction
+
 2019-01-09  Jiri Vanek <jvanek@redhat.com>
 
 	With hope to backport windows desktop support and portable launchers from 1.8, adapted makefiles to be like 1.8 
--- a/Makefile.am	Wed Jan 09 17:41:55 2019 +0100
+++ b/Makefile.am	Mon Feb 25 13:41:58 2019 +0100
@@ -168,9 +168,17 @@
 
 # Conditional defintions
 if HAVE_TAGSOUP
+if HAVE_MSLINKS
   NETX_EXCLUDE_SRCS=
 else
+  NETX_EXCLUDE_SRCS=net.sourceforge.jnlp.util.WindowsDesktopEntry.java
+endif
+else
+if HAVE_MSLINKS
   NETX_EXCLUDE_SRCS=net.sourceforge.jnlp.MalformedXMLParser.java
+else
+  NETX_EXCLUDE_SRCS=net.sourceforge.jnlp.MalformedXMLParser.java net.sourceforge.jnlp.util.WindowsDesktopEntry.java
+endif
 endif
 
 # Flags
@@ -187,7 +195,7 @@
 #    IllegalAccessException
 #  - we want full privileges
 #
-export UNIFIED_CLASSPATH_SEGMENTS=$(NETX_JAR) $(PLUGIN_JAR) $(JSOBJECT_JAR) $(RHINO_JAR) $(TAGSOUP_JAR)
+export UNIFIED_CLASSPATH_SEGMENTS=$(NETX_JAR) $(PLUGIN_JAR) $(JSOBJECT_JAR) $(RHINO_JAR) $(TAGSOUP_JAR) $(MSLINKS_JAR)
 export UNIFIED_JACOCO_CLASSPATH_SEGMENTS=$(UNIFIED_CLASSPATH_SEGMENTS) $(JACOCO_CLASSPATH)
 export UNIFIED_CLASSPATH=$(call joinsegments, $(UNIFIED_CLASSPATH_SEGMENTS))
 export UNIFIED_JACOCO_CLASSPATH=$(call joinsegments, $(UNIFIED_JACOCO_CLASSPATH_SEGMENTS))
@@ -323,6 +331,7 @@
   -e "s|[@]NETX_JAR[@]|$(NETX_JAR)|g" \
   -e "s|[@]PLUGIN_JAR[@]|$(PLUGIN_JAR)|g" \
   -e "s|[@]JSOBJECT_JAR[@]|$(JSOBJECT_JAR)|g" \
+  -e "s|[@]MSLINKS_JAR[@]|$(MSLINKS_JAR)|g" \
   -e "s|[@]TAGSOUP_JAR[@]|$(TAGSOUP_JAR)|g" \
   -e "s|[@]RHINO_JAR[@]|$(RHINO_JAR)|g" \
   -e "s|[@]PROGRAM_NAME[@]|$${PROGRAM_NAME}|g"
@@ -421,6 +430,10 @@
 	filteredName=`basename $(TAGSOUP_JAR) | sed "s/[^a-zA-Z]//g" | sed "s/jar$$/.jar/"` ; \
 	cp -v $(TAGSOUP_JAR) $(WIN_RUN_DEPS)/$$filteredName
 endif
+if HAVE_MSLINKS
+	filteredName=`basename $(MSLINKS_JAR) | sed "s/[^a-zA-Z]//g" | sed "s/jar$$/.jar/"` ; \
+	cp -v $(MSLINKS_JAR) $(WIN_RUN_DEPS)/$$filteredName
+endif
 endif
 
 #no npapi plugin at all!
@@ -695,7 +708,7 @@
 	  mkdir -p $(TOP_BUILD_DIR)/liveconnect && \
 	  $(SYSTEM_JDK_DIR)/bin/javac $(IT_JAVACFLAGS) \
 	      -d $(TOP_BUILD_DIR)/liveconnect \
-	      $(call composeclasspath,$(NETX_DIR) $(TAGSOUP_JAR) $(RHINO_JAR)) \
+	      $(call composeclasspath,$(NETX_DIR) $(MSLINKS_JAR) $(TAGSOUP_JAR) $(RHINO_JAR)) \
 	      -sourcepath $(LIVECONNECT_SRCS) \
 	      @liveconnect-source-files.txt ; \
 	fi
@@ -854,7 +867,7 @@
 	$(SYSTEM_JDK_DIR)/bin/javac $(IT_JAVACFLAGS) \
 		-d $(NETX_DIR) \
 		-sourcepath $(NETX_SRCDIR) \
-		$(call composeclasspath, $(TAGSOUP_JAR) $(RHINO_JAR)) \
+		$(call composeclasspath, $(MSLINKS_JAR) $(TAGSOUP_JAR) $(RHINO_JAR)) \
 	    @netx-source-files.txt
 	(cd $(NETX_RESOURCE_DIR); \
 	 for files in $$(find . -type f); \
@@ -993,7 +1006,7 @@
 	 -doctitle 'IcedTea-Web: NetX API Specification' \
 	 -windowtitle 'IcedTea-Web: NetX ' \
 	 -header '<strong>IcedTea-Web<br/>NetX</strong>' \
-	 $(call composeclasspath, $(TAGSOUP_JAR) $(RHINO_JAR)) \
+	 $(call composeclasspath, $(MSLINKS_JAR) $(TAGSOUP_JAR) $(RHINO_JAR)) \
 	 $(NETX_PKGS)
 endif
 	mkdir -p stamps
@@ -1011,7 +1024,7 @@
 	 -doctitle 'IcedTea-Web: Plugin API Specification' \
 	 -windowtitle 'IcedTea-Web: Plugin ' \
 	 -header '<strong>IcedTea-Web<br/>Plugin</strong>' \
-	 $(call composeclasspath, $(TAGSOUP_JAR) $(RHINO_JAR)) \
+	 $(call composeclasspath, $(MSLINKS_JAR) $(TAGSOUP_JAR) $(RHINO_JAR)) \
 	 $(PLUGIN_PKGS)
 endif
 endif
@@ -1491,7 +1504,7 @@
 	mkdir -p $(NETX_UNIT_TEST_DIR) && \
 	$(SYSTEM_JDK_DIR)/bin/javac $(IT_JAVACFLAGS) \
 	 -d $(NETX_UNIT_TEST_DIR) \
-	 $(call composeclasspath, $(JUNIT_RUNTIME) $(TOP_BUILD_DIR)/liveconnect/lib/classes.jar $(NETX_DIR)/lib/classes.jar $(TEST_EXTENSIONS_DIR) $(TAGSOUP_JAR)) \
+	 $(call composeclasspath, $(JUNIT_RUNTIME) $(TOP_BUILD_DIR)/liveconnect/lib/classes.jar $(NETX_DIR)/lib/classes.jar $(MSLINKS_JAR)  $(TEST_EXTENSIONS_DIR) $(TAGSOUP_JAR)) \
 	 @netx-unit-tests-source-files.txt && \
 	mkdir -p stamps && \
 	touch $@
@@ -1521,7 +1534,7 @@
 	done ; \
 	cd $(NETX_UNIT_TEST_DIR) ; \
 	class_names=`cat $(UNIT_CLASS_NAMES)` ; \
-	CLASSPATH="$(call joinsegments, $(NETX_DIR)/lib/classes.jar $(TOP_BUILD_DIR)/liveconnect/lib/classes.jar $(JUNIT_RUNTIME) $(JUNIT_RUNNER_JAR) $(TEST_EXTENSIONS_DIR) . $(TEST_EXTENSIONS_SRCDIR) $(TAGSOUP_JAR))" ; \
+	CLASSPATH="$(call joinsegments, $(NETX_DIR)/lib/classes.jar $(TOP_BUILD_DIR)/liveconnect/lib/classes.jar $(JUNIT_RUNTIME) $(MSLINKS_JAR) $(JUNIT_RUNNER_JAR) $(TEST_EXTENSIONS_DIR) . $(TEST_EXTENSIONS_SRCDIR) $(TAGSOUP_JAR))" ; \
 	  $(SYSTEM_JRE_DIR)/bin/java "-Xbootclasspath/a:$$CLASSPATH" CommandLine $$class_names 
 if WITH_XSLTPROC
 	-$(XSLTPROC) --stringparam logs logs_unit.html $(TESTS_SRCDIR)/$(REPORT_STYLES_DIRNAME)/jreport.xsl $(NETX_UNIT_TEST_DIR)/tests-output.xml > $(TESTS_DIR)/index_unit.html
@@ -1561,7 +1574,7 @@
 	  mv $(NETX_UNIT_TEST_DIR)/$$file  $(NETX_UNIT_TEST_DIR)/"$$file""$(EMMA_BACKUP_SUFFIX)" ; \
 	done ;\
 	class_names=`cat $(UNIT_CLASS_NAMES)` ; \
-	CLASSPATH=$(call joinsegments, $(NETX_DIR)/lib/classes.jar $(TOP_BUILD_DIR)/liveconnect/lib/classes.jar $(JUNIT_RUNTIME) $(JUNIT_RUNNER_JAR) $(TEST_EXTENSIONS_DIR) $(JACOCO_CLASSPATH) . $(TEST_EXTENSIONS_SRCDIR) $(TAGSOUP_JAR)) ; \
+	CLASSPATH=$(call joinsegments, $(NETX_DIR)/lib/classes.jar $(TOP_BUILD_DIR)/liveconnect/lib/classes.jar $(JUNIT_RUNTIME) $(JUNIT_RUNNER_JAR) $(TEST_EXTENSIONS_DIR) $(JACOCO_CLASSPATH) . $(TEST_EXTENSIONS_SRCDIR) $(MSLINKS_JAR) $(TAGSOUP_JAR)) ; \
 	  $(SYSTEM_JRE_DIR)/bin/java $(JACOCO_AGENT_SWITCH) -Xbootclasspath/a:$$CLASSPATH CommandLine $$class_names ; \
 	for file in $(COVERAGE_MODIFIED_FILES) ; do \
 	  mv $(NETX_UNIT_TEST_DIR)/$$file  $(NETX_UNIT_TEST_DIR)/"$$file""$(EMMA_SUFFIX)" ; \
--- a/NEWS	Wed Jan 09 17:41:55 2019 +0100
+++ b/NEWS	Mon Feb 25 13:41:58 2019 +0100
@@ -14,6 +14,8 @@
 * enhanced to allow resources to be read also from j2se/java element (OmegaT)
 * PR3644 - java.lang.NoClassDefFoundError: Could not initialize class net.sourceforge.jnlp.runtime.JNLPRuntime$DeploymentConfigurationHolder
 * deployment.config now support generic url instead just file
+* Added support for windows desktop shortcuts via https://github.com/DmitriiShamrikov/mslinks
+* cache can now be operated by groups, list by -Xcacheids (details via -verbose, can filter by regex), Xclearcache now can clear only selected id. There is also gui to operate cache via id in itweb-settings now.
 
 New in release 1.7.1 (2017-12-15):
 * better work with authors file
--- a/acinclude.m4	Wed Jan 09 17:41:55 2019 +0100
+++ b/acinclude.m4	Mon Feb 25 13:41:58 2019 +0100
@@ -492,6 +492,43 @@
   AM_CONDITIONAL([HAVE_TAGSOUP], [test x$TAGSOUP_JAR != xno -a x$TAGSOUP_JAR != x ])
 ])
 
+
+AC_DEFUN_ONCE([IT_CHECK_FOR_MSLINKS],
+[
+  AC_MSG_CHECKING([for mslinks])
+  AC_ARG_WITH([mslinks],
+             [AS_HELP_STRING([--with-mslinks],
+                             [mslinks.jar])],
+             [
+                MSLINKS_JAR=${withval}
+             ],
+             [
+                MSLINKS_JAR=
+             ])
+  if test -z "${MSLINKS_JAR}"; then
+    for dir in /usr/share/java /usr/local/share/java ; do
+      if test -f $dir/mslinks.jar; then
+        MSLINKS_JAR=$dir/mslinks.jar
+	    break
+      fi
+    done
+  fi
+  AM_COND_IF([WINDOWS], [
+    MSLINKS_JAR=$(cygpath -m ${MSLINKS_JAR})
+  ])
+  AC_MSG_RESULT(${MSLINKS_JAR})
+  AM_COND_IF([WINDOWS], [
+    if test -z "${MSLINKS_JAR}"; then
+      AC_MSG_RESULT(**********************************************)
+      AC_MSG_RESULT(*  Warning you are building without mslinks  *)
+      AC_MSG_RESULT(* Your windows desktop integration will fail *)
+      AC_MSG_RESULT(**********************************************)
+    fi
+  ])
+  AC_SUBST(MSLINKS_JAR)
+  AM_CONDITIONAL([HAVE_MSLINKS], [test x$MSLINKS_JAR != xno -a x$MSLINKS_JAR != x ])
+])
+
 dnl Generic macro to check for a Java class
 dnl Takes the name of the class as an argument.  The macro name
 dnl is usually the name of the class with '.'
--- a/configure.ac	Wed Jan 09 17:41:55 2019 +0100
+++ b/configure.ac	Mon Feb 25 13:41:58 2019 +0100
@@ -158,6 +158,7 @@
     [/usr/share/java/objectweb-asm4/asm-all.jar /usr/share/java/objectweb-asm4/asm-all-4.0.jar /usr/share/java/objectweb-asm/asm-all.jar])
 
 IT_CHECK_FOR_TAGSOUP
+IT_CHECK_FOR_MSLINKS
 
 if test "x$build_windows" = xyes ; then
   IT_CHECK_FOR_WIX
--- a/launcher/launchers.bat.in	Wed Jan 09 17:41:55 2019 +0100
+++ b/launcher/launchers.bat.in	Mon Feb 25 13:41:58 2019 +0100
@@ -56,7 +56,7 @@
   if not "%INST_ITW_HOME%" == ""  (
   set SPLASH_LOCATION=%INST_ITW_HOME%/share/icedtea-web/javaws_splash.png
   set BINARY_LOCATION=%INST_ITW_HOME%/bin/@PROGRAM_NAME@.bat
-  set LAUNCHER_BOOTCLASSPATH=-Xbootclasspath/a:%INST_ITW_HOME%/share/icedtea-web/netx.jar;%INST_ITW_HOME%/share/icedtea-web/plugin.jar;%INST_ITW_HOME%/share/icedtea-web/jsobject.jar;%INST_ITW_HOME%/win-deps-runtime/js.jar;%INST_ITW_HOME%/win-deps-runtime/tagsoup.jar;%NASHORN%
+  set LAUNCHER_BOOTCLASSPATH=-Xbootclasspath/a:%INST_ITW_HOME%/share/icedtea-web/netx.jar;%INST_ITW_HOME%/share/icedtea-web/plugin.jar;%INST_ITW_HOME%/share/icedtea-web/jsobject.jar;%INST_ITW_HOME%/win-deps-runtime/js.jar;%INST_ITW_HOME%/win-deps-runtime/tagsoup.jar;%INST_ITW_HOME%/win-deps-runtime/mslinks.jar;%NASHORN%
   ) else (
     set SPLASH_LOCATION=@JAVAWS_SPLASH_LOCATION@
     set BINARY_LOCATION=@BIN_LOCATION@.bat
@@ -65,7 +65,7 @@
 ) else (
   set SPLASH_LOCATION=%ITW_HOME%/share/icedtea-web/javaws_splash.png
   set BINARY_LOCATION=%ITW_HOME%/bin/@PROGRAM_NAME@.bat
-  set LAUNCHER_BOOTCLASSPATH=-Xbootclasspath/a:%ITW_HOME%/share/icedtea-web/netx.jar;%ITW_HOME%/share/icedtea-web/plugin.jar;%ITW_HOME%/share/icedtea-web/jsobject.jar;%ITW_HOME%/win-deps-runtime/js.jar;%ITW_HOME%/win-deps-runtime/tagsoup.jar;%NASHORN%
+  set LAUNCHER_BOOTCLASSPATH=-Xbootclasspath/a:%ITW_HOME%/share/icedtea-web/netx.jar;%ITW_HOME%/share/icedtea-web/plugin.jar;%ITW_HOME%/share/icedtea-web/jsobject.jar;%ITW_HOME%/win-deps-runtime/js.jar;%ITW_HOME%/win-deps-runtime/tagsoup.jar;%ITW_HOME%/win-deps-runtime/mslinks.jar;%NASHORN%
 )
 
 
--- a/netx/net/sourceforge/jnlp/Launcher.java	Wed Jan 09 17:41:55 2019 +0100
+++ b/netx/net/sourceforge/jnlp/Launcher.java	Mon Feb 25 13:41:58 2019 +0100
@@ -83,6 +83,8 @@
     private ParserSettings parserSettings = new ParserSettings();
 
     private Map<String, List<String>> extra = null;
+    
+    public static final String KEY_JAVAWS_LOCATION = "icedtea-web.bin.location";
 
     /**
      * Create a launcher with the runtime's default update policy
@@ -422,7 +424,7 @@
             List<String> commands = new LinkedList<>();
 
             // this property is set by the javaws launcher to point to the javaws binary
-            String pathToWebstartBinary = System.getProperty("icedtea-web.bin.location");
+            String pathToWebstartBinary = System.getProperty(KEY_JAVAWS_LOCATION);
             commands.add(pathToWebstartBinary);
             // use -Jargument format to pass arguments to the JVM through the launcher
             for (String arg : vmArgs) {
--- a/netx/net/sourceforge/jnlp/OptionsDefinitions.java	Wed Jan 09 17:41:55 2019 +0100
+++ b/netx/net/sourceforge/jnlp/OptionsDefinitions.java	Mon Feb 25 13:41:58 2019 +0100
@@ -53,7 +53,8 @@
         //javaws control-options
         ABOUT("-about", "BOAbout"),
         VIEWER("-viewer", "BOViewer"),
-        CLEARCACHE("-Xclearcache", "BXclearcache"),
+        CLEARCACHE("-Xclearcache", "BXclearcache", NumberOfArguments.NONE_OR_ONE),
+        LISTCACHEIDS("-Xcacheids", "BXcacheids", NumberOfArguments.NONE_OR_ONE),
         LICENSE("-license", "BOLicense"),
         HELP1("-help", "BOHelp1"),
         //javaws run-options
@@ -148,6 +149,7 @@
         NONE("NOAnone"),
         ONE("NOAone"),
         ONE_OR_MORE("NOAonemore"),
+        NONE_OR_ONE("NOAnonorone"),
         EVEN_NUMBER_SUPPORTS_EQUALS_CHAR("NOAevennumber");
 
         String messageKey;
@@ -194,6 +196,7 @@
             OPTIONS.ABOUT,
             OPTIONS.VIEWER,
             OPTIONS.CLEARCACHE,
+            OPTIONS.LISTCACHEIDS,
             OPTIONS.LICENSE,
             OPTIONS.HELP1}
         );
--- a/netx/net/sourceforge/jnlp/cache/CacheDirectory.java	Wed Jan 09 17:41:55 2019 +0100
+++ b/netx/net/sourceforge/jnlp/cache/CacheDirectory.java	Mon Feb 25 13:41:58 2019 +0100
@@ -46,6 +46,8 @@
 
     /* Don't allow instantiation of this class */
     private CacheDirectory(){}
+    
+    public static final String INFO_SUFFIX = ".info";
 
     /**
      * Get the structure of directory for keeping track of the protocol and
@@ -56,7 +58,7 @@
     public static void getDirStructure(DirectoryNode root) {
         for (File f : root.getFile().listFiles()) {
             DirectoryNode node = new DirectoryNode(f.getName(), f, root);
-            if (f.isDirectory() || (!f.isDirectory() && !f.getName().endsWith(".info")))
+            if (f.isDirectory() || (!f.isDirectory() && !f.getName().endsWith(INFO_SUFFIX)))
                 root.addChild(node);
             if (f.isDirectory())
                 getDirStructure(node);
@@ -74,7 +76,7 @@
         for (DirectoryNode f : root.getChildren()) {
             if (f.isDir())
                 temp.addAll(getLeafData(f));
-            else if (!f.getName().endsWith(".info"))
+            else if (!f.getName().endsWith(INFO_SUFFIX))
                 temp.add(f);
         }
         return temp;
--- a/netx/net/sourceforge/jnlp/cache/CacheEntry.java	Wed Jan 09 17:41:55 2019 +0100
+++ b/netx/net/sourceforge/jnlp/cache/CacheEntry.java	Mon Feb 25 13:41:58 2019 +0100
@@ -39,6 +39,7 @@
     private static final String KEY_CONTENT_ORIGINAL_LENGTH = "content-original-length";
     private static final String KEY_LAST_MODIFIED = "last-modified";
     private static final String KEY_LAST_UPDATED = "last-updated";
+    public static final String KEY_JNLP_PATH = "jnlp-path";
 
     /** the remote resource location */
     private final URL location;
@@ -68,7 +69,7 @@
      */
     PropertiesFile readCacheEntryInfo() {
         File infoFile = CacheUtil.getCacheFile(location, version);
-        infoFile = new File(infoFile.getPath() + ".info"); // replace with something that can't be clobbered
+        infoFile = new File(infoFile.getPath() + CacheDirectory.INFO_SUFFIX); // replace with something that can't be clobbered
 
         return new PropertiesFile(infoFile, R("CAutoGen"));
     }
@@ -107,6 +108,10 @@
         setLongKey(KEY_CONTENT_LENGTH, length);
     }
 
+    public void setJnlpPath(String jnlpPath) {
+    	properties.setProperty(KEY_JNLP_PATH, jnlpPath);
+    }
+    
     /**
      * Return the length of the original content that was cached. May be different
      * from the actual cache entry size due to (de)compression.
--- a/netx/net/sourceforge/jnlp/cache/CacheLRUWrapper.java	Wed Jan 09 17:41:55 2019 +0100
+++ b/netx/net/sourceforge/jnlp/cache/CacheLRUWrapper.java	Mon Feb 25 13:41:58 2019 +0100
@@ -70,6 +70,7 @@
     
     private final InfrastructureFileDescriptor recentlyUsedPropertiesFile;
     private final InfrastructureFileDescriptor cacheDir;
+    private final File windowsShortcutList;
     
     public CacheLRUWrapper() {
         this(PathsAndFiles.getRecentlyUsedFile(), PathsAndFiles.CACHE_DIR);
@@ -81,9 +82,10 @@
      * @param recentlyUsed file to be used as recently_used file
      * @param cacheDir dir with cache
      */
-    public CacheLRUWrapper(final InfrastructureFileDescriptor recentlyUsed, final InfrastructureFileDescriptor cacheDir) {
+    CacheLRUWrapper(final InfrastructureFileDescriptor recentlyUsed, final InfrastructureFileDescriptor cacheDir) {
         recentlyUsedPropertiesFile = recentlyUsed;
         this.cacheDir = cacheDir;
+        windowsShortcutList = new File(cacheDir.getFile(), "shortcutList.txt");
         if (!recentlyUsed.getFile().exists()) {
             try {
                 FileUtils.createParentDir(recentlyUsed.getFile());
@@ -136,6 +138,10 @@
         return cacheDir;
     }
 
+    public File getWindowsShortcutList() {
+        return windowsShortcutList;
+    }
+    
     /**
      * @return the recentlyUsedFile
      */
--- a/netx/net/sourceforge/jnlp/cache/CacheUtil.java	Wed Jan 09 17:41:55 2019 +0100
+++ b/netx/net/sourceforge/jnlp/cache/CacheUtil.java	Mon Feb 25 13:41:58 2019 +0100
@@ -29,23 +29,33 @@
 import java.net.URLConnection;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
+import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.Permission;
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 import javax.jnlp.DownloadServiceListener;
 
 import net.sourceforge.jnlp.Version;
 import net.sourceforge.jnlp.config.DeploymentConfiguration;
 import net.sourceforge.jnlp.config.PathsAndFiles;
+import net.sourceforge.jnlp.controlpanel.CachePane;
 import net.sourceforge.jnlp.runtime.ApplicationInstance;
 import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.runtime.Translator;
 import static net.sourceforge.jnlp.runtime.Translator.R;
 
 import net.sourceforge.jnlp.security.ConnectionFactory;
@@ -141,15 +151,11 @@
      * @return true if the cache could be cleared and was cleared
      */
     public static boolean clearCache() {
-
-        if (!okToClearCache()) {
-            OutputController.getLogger().log(OutputController.Level.ERROR_ALL, R("CCannotClearCache"));
-            return false;
-        }
-        
+        // clear all cache
         CacheLRUWrapper lruHandler = CacheLRUWrapper.getInstance();
         File cacheDir = lruHandler.getCacheDir().getFile();
-        if (!(cacheDir.isDirectory())) {
+
+        if (!checkToClearCache()) {
             return false;
         }
 
@@ -157,6 +163,10 @@
         lruHandler.lock();
         try {
             cacheDir = cacheDir.getCanonicalFile();
+            // remove windows shortcuts before cache dir is gone
+            if (JNLPRuntime.isWindows()) {
+                removeWindowsShortcuts("ALL");
+            }
             FileUtils.recursiveDelete(cacheDir, cacheDir);
             cacheDir.mkdir();
             lruHandler.clearLRUSortedEntries();
@@ -169,6 +179,188 @@
         return true;
     }
 
+    public static boolean clearCache(final String application) {
+        // clear one app
+        if (!checkToClearCache()) {
+            return false;
+        }
+
+        OutputController.getLogger().log(OutputController.Level.WARNING_ALL, Translator.R("BXSingleCacheCleared", application));
+        List<CacheId> ids = getCacheIds(".*");
+        int found = 0;
+        int files = 0;
+        for (CacheId id : ids) {
+            if (id.getId().equalsIgnoreCase(application)) {
+                found++;
+                files += id.files.size();
+            }
+        }
+        if (found == 0) {
+            OutputController.getLogger().log(OutputController.Level.ERROR_ALL, Translator.R("BXSingleCacheClearNotFound", application));
+        }
+        if (found > 1) {
+            OutputController.getLogger().log(OutputController.Level.ERROR_ALL, Translator.R("BXSingleCacheMoreThenOneId", application));
+        }
+        OutputController.getLogger().log(OutputController.Level.MESSAGE_ALL, Translator.R("BXSingleCacheFileCount", files));
+        CacheLRUWrapper.getInstance().lock();
+        try {
+            Files.walk(Paths.get(CacheLRUWrapper.getInstance().getCacheDir().getFile().getCanonicalPath())).filter(new Predicate<Path>() {
+                @Override
+                public boolean test(Path t) {
+                    return Files.isRegularFile(t);
+                }
+            }).forEach(new Consumer<Path>() {
+                @Override
+                public void accept(Path path) {
+                    if (path.getFileName().toString().endsWith(CacheDirectory.INFO_SUFFIX)) {
+                        PropertiesFile pf = new PropertiesFile(new File(path.toString()));
+                        // if jnlp-path in .info equals path of app to delete mark to delete
+                        String jnlpPath = pf.getProperty(CacheEntry.KEY_JNLP_PATH);
+                        if (application.equalsIgnoreCase(jnlpPath) || application.equalsIgnoreCase(getDomain(path))) {
+                            pf.setProperty("delete", "true");
+                            pf.store();
+                            OutputController.getLogger().log("marked for deletion: " + path);
+                        }
+                    }
+                }
+            });
+            if (JNLPRuntime.isWindows()) {
+                removeWindowsShortcuts(application.toLowerCase());
+            }
+            // clean the cache of entries now marked for deletion
+            cleanCache();
+
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            CacheLRUWrapper.getInstance().unlock();
+        }
+        return true;
+    }
+
+    public static boolean checkToClearCache() {
+        if (!okToClearCache()) {
+            OutputController.getLogger().log(OutputController.Level.ERROR_ALL, R("CCannotClearCache"));
+            return false;
+        }
+        return CacheLRUWrapper.getInstance().getCacheDir().getFile().isDirectory();
+    }
+
+    public static void removeWindowsShortcuts(String jnlpApp)
+            throws IOException {
+        OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, "Clearing Windows shortcuts");
+        if (CacheLRUWrapper.getInstance().getWindowsShortcutList().exists()) {
+            List<String> lines = Files.readAllLines(CacheLRUWrapper.getInstance().getWindowsShortcutList().toPath(), Charset.forName("UTF-8"));
+            Iterator it = lines.iterator();
+            Boolean fDelete;
+            while (it.hasNext()) {
+                String sItem = it.next().toString();
+                String[] sArray = sItem.split(",");
+                String application = sArray[0];
+                String sPath = sArray[1];
+                // if application is codebase then delete files
+                if (application.equalsIgnoreCase(jnlpApp)) {
+                    fDelete = true;
+                    it.remove();
+                } else {
+                    fDelete = false;
+                }
+                if (jnlpApp.equals("ALL")) {
+                    fDelete = true;
+                }
+                if (fDelete) {
+                    OutputController.getLogger().log("Deleting item = " + sPath);
+                    File scList = new File(sPath);
+                    try {
+                        FileUtils.recursiveDelete(scList, scList);
+                    } catch (Exception e) {
+                        OutputController.getLogger().log(e);
+                    }
+                }
+            }
+            if (jnlpApp.equals("ALL")) {
+                //delete shortcut list file
+                Files.deleteIfExists(CacheLRUWrapper.getInstance().getWindowsShortcutList().toPath());
+            } else {
+                //write file after application scuts have been removed
+                Files.write(CacheLRUWrapper.getInstance().getWindowsShortcutList().toPath(), lines, Charset.forName("UTF-8"));
+            }
+        }
+
+    }
+    
+     public static void listCacheIds(String filter) {
+         List<CacheId> items = getCacheIds(filter);
+         if (JNLPRuntime.isDebug()) {
+             for (CacheId id : items) {
+                 OutputController.getLogger().log(OutputController.Level.MESSAGE_ALL, id.getId()+" ("+id.getType()+") ["+id.files.size()+"]");
+                 for(Object[] o: id.getFiles()){
+                     StringBuilder sb = new StringBuilder();
+                     for (int i = 0; i < o.length; i++) {
+                         Object object = o[i];
+                         if (object == null) {
+                             object = "??";
+                         }
+                         sb.append(object.toString()).append(" ;  ");
+                     }
+                     OutputController.getLogger().log(OutputController.Level.MESSAGE_ALL, "  * " + sb);
+                 }
+             }
+         } else {
+             for (CacheId id : items) {
+                 OutputController.getLogger().log(OutputController.Level.MESSAGE_ALL, id.getId());
+             }
+         }
+     }
+     
+     /**
+      * This method load all known IDs of applications and  will gather all members, which share the id
+      * @return 
+      */
+      public static List<CacheId> getCacheIds(final String filter) {
+        CacheLRUWrapper.getInstance().lock();
+        final List<CacheId> r = new ArrayList<CacheId>();
+        try {
+            Files.walk(Paths.get(CacheLRUWrapper.getInstance().getCacheDir().getFile().getCanonicalPath())).filter(new Predicate<Path>() {
+                @Override
+                public boolean test(Path t) {
+                    return Files.isRegularFile(t);
+                }
+            }).forEach(new Consumer<Path>() {
+                @Override
+                public void accept(Path path) {
+                    if (path.getFileName().toString().endsWith(CacheDirectory.INFO_SUFFIX)) {
+                        PropertiesFile pf = new PropertiesFile(new File(path.toString()));
+                        // if jnlp-path in .info equals path of app to delete mark to delete
+                        String jnlpPath = pf.getProperty(CacheEntry.KEY_JNLP_PATH);
+                        if (jnlpPath != null && jnlpPath.matches(filter)) {
+                            CacheId jnlpPathId = new CacheJnlpId(jnlpPath);
+                            if (!r.contains(jnlpPathId)) {
+                                r.add(jnlpPathId);
+                                jnlpPathId.populate();
+
+                            }
+                        }
+                        String domain = getDomain(path);
+                        if (domain != null && domain.matches(filter)) {
+                            CacheId doaminId = new CacheDomainId(domain);
+                            if (!r.contains(doaminId)) {
+                                r.add(doaminId);
+                                doaminId.populate();
+
+                            }
+                        }
+                    }
+                }
+            });
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            CacheLRUWrapper.getInstance().unlock();
+        }
+        return r;
+    }
+
     /**
      * Returns a boolean indicating if it ok to clear the netx application cache at this point
      * @return true if the cache can be cleared at this time without problems
@@ -395,7 +587,7 @@
                         try {
                             cacheFile = urlToPath(source, path);
                             FileUtils.createParentDir(cacheFile);
-                            File pf = new File(cacheFile.getPath() + ".info");
+                            File pf = new File(cacheFile.getPath() + CacheDirectory.INFO_SUFFIX);
                             FileUtils.createRestrictedFile(pf, true); // Create the info file for marking later.
                             lruHandler.addEntry(lruHandler.generateKey(cacheFile.getPath()), cacheFile.getPath());
                         } catch (IOException ioe) {
@@ -630,7 +822,7 @@
                     final String path = e.getValue();
 
                     File file = new File(path);
-                    PropertiesFile pf = new PropertiesFile(new File(path + ".info"));
+                    PropertiesFile pf = new PropertiesFile(new File(path + CacheDirectory.INFO_SUFFIX));
                     boolean delete = Boolean.parseBoolean(pf.getProperty("delete"));
 
                 /*
@@ -695,4 +887,135 @@
             }
         }
     }
+
+    static class CacheJnlpId extends CacheId {
+
+        public CacheJnlpId(String id) {
+            super(id);
+        }
+
+        @Override
+        public void populate() {
+            ArrayList<Object[]> all = CachePane.generateData();
+            for (Object[] object : all) {
+                if (id.equals(object[6])) {
+                    this.files.add(object);
+                }
+            }
+        }
+
+        @Override
+        String getType() {
+            return "JNLP-PATH";
+        }
+
+        @Override
+        //hascode in super is ok
+        public boolean equals(Object obj) {
+            if (obj instanceof CacheJnlpId) {
+                return super.equals(obj);
+            } else {
+                return false;
+            }
+        }
+
+    }
+
+    static class CacheDomainId extends CacheId {
+
+        public CacheDomainId(String id) {
+            super(id);
+        }
+
+        @Override
+        public void populate() {
+            ArrayList<Object[]> all = CachePane.generateData();
+            for (Object[] object : all) {
+                if (id.equals(object[3].toString())) {
+                    this.files.add(object);
+                }
+            }
+        }
+
+        @Override
+        String getType() {
+            return "DOMAIN";
+        }
+
+        @Override
+        //hascode in super is ok
+        public boolean equals(Object obj) {
+            if (obj instanceof CacheDomainId) {
+                return super.equals(obj);
+            } else {
+                return false;
+            }
+        }
+
+    }
+
+    public abstract static class CacheId {
+
+        //last century array of objects instead of some nice class inherited from previous century
+        protected final List<Object[]> files = new ArrayList<>();
+
+        abstract void populate();
+
+        abstract String getType();
+
+        protected final String id;
+
+        public CacheId(String id) {
+            this.id = id;
+        }
+
+        @Override
+        public String toString() {
+            return id;
+        }   
+
+        public List<Object[]> getFiles() {
+            return files;
+        }
+
+        public String getId() {
+            return id;
+        }
+        
+        
+        
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof CacheId) {
+                CacheId c = (CacheId) obj;
+                if (c.id == null && this.id == null) {
+                    return true;
+                }
+                if (c.id == null) {
+                    return false;
+                }
+                return c.id.equals(this.id);
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(this.id);
+        }
+
+    }
+
+    private static String getDomain(Path path) {
+        String relativeToCache = path.toAbsolutePath().toString().replace(CacheLRUWrapper.getInstance().getCacheDir().getFullPath(), "");
+        for (int x = 0; x < 3; x++) {
+            int i = relativeToCache.indexOf(File.separator);
+            relativeToCache = relativeToCache.substring(i + 1);
+        }
+        int i = relativeToCache.indexOf(File.separator);
+        relativeToCache = relativeToCache.substring(0, i);
+        return relativeToCache;
+    }
 }
--- a/netx/net/sourceforge/jnlp/cache/DirectoryNode.java	Wed Jan 09 17:41:55 2019 +0100
+++ b/netx/net/sourceforge/jnlp/cache/DirectoryNode.java	Mon Feb 25 13:41:58 2019 +0100
@@ -84,7 +84,7 @@
             this.childNodes = new ArrayList<DirectoryNode>();
         this.parent = parent;
         if (!isDir())
-            this.infoFile = new File(this.getFile().getAbsolutePath().concat(".info"));
+            this.infoFile = new File(this.getFile().getAbsolutePath().concat(CacheDirectory.INFO_SUFFIX));
     }
 
     /**
--- a/netx/net/sourceforge/jnlp/cache/ResourceDownloader.java	Wed Jan 09 17:41:55 2019 +0100
+++ b/netx/net/sourceforge/jnlp/cache/ResourceDownloader.java	Mon Feb 25 13:41:58 2019 +0100
@@ -29,7 +29,9 @@
 import java.util.zip.GZIPInputStream;
 
 import net.sourceforge.jnlp.DownloadOptions;
+import net.sourceforge.jnlp.OptionsDefinitions;
 import net.sourceforge.jnlp.Version;
+import net.sourceforge.jnlp.runtime.Boot;
 import net.sourceforge.jnlp.runtime.JNLPRuntime;
 import net.sourceforge.jnlp.security.ConnectionFactory;
 import net.sourceforge.jnlp.security.SecurityDialogs;
@@ -160,7 +162,6 @@
             if (lm == null) {
                 lm = connection.getLastModified();
             }
-
             boolean current = CacheUtil.isCurrent(resource.getLocation(), resource.getRequestVersion(), lm) && resource.getUpdatePolicy() != UpdatePolicy.FORCE;
             if (!current) {
                 if (entry.isCached()) {
@@ -192,8 +193,33 @@
                 entry.setRemoteContentLength(size);
                 entry.setLastModified(lm);
             }
-
             entry.setLastUpdated(System.currentTimeMillis());
+            try { 
+                //do not die here no metter of cost. Just metadata
+                //is the path from user best to store? He can run some jnlp from temp which then be stored
+                //on contrary, this downloads the jnlp, we actually do not have jnlp parsed during first interaction
+                //in addition, downloaded name can be really nasty (some generated has from dynamic servlet.jnlp)
+                //anjother issue is forking. If this (eg local) jnlp starts its second isntance, the url *can* be different
+                //in contrary, usally si no. as fork is reusing all args, and only adding xmx/xms and xnofork.
+                String jnlpPath = Boot.getOptionParser().getMainArg(); //get jnlp from args passed 
+                if (jnlpPath == null || jnlpPath.equals("")) {
+                    jnlpPath = Boot.getOptionParser().getParam(OptionsDefinitions.OPTIONS.JNLP);
+                    if (jnlpPath == null || jnlpPath.equals("")) {
+                        jnlpPath = Boot.getOptionParser().getParam(OptionsDefinitions.OPTIONS.HTML);
+                        if (jnlpPath == null || jnlpPath.equals("")) {
+                            OutputController.getLogger().log(OutputController.Level.MESSAGE_ALL, "Not-setting jnlp-path for missing main/jnlp/html argument");
+                        } else {
+                            entry.setJnlpPath(jnlpPath);
+                        }
+                    } else {
+                        entry.setJnlpPath(jnlpPath);
+                    }
+                } else {
+                    entry.setJnlpPath(jnlpPath);
+                }
+            } catch (Exception ex){
+                OutputController.getLogger().log(OutputController.Level.ERROR_ALL, ex);
+            }
             entry.store();
 
             synchronized (lock) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/netx/net/sourceforge/jnlp/controlpanel/CacheAppViewer.java	Mon Feb 25 13:41:58 2019 +0100
@@ -0,0 +1,163 @@
+/* CacheViewer.java -- Display the GUI for viewing and deleting cache files.
+Copyright (C) 2013 Red Hat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package net.sourceforge.jnlp.controlpanel;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.List;
+import javax.swing.JButton;
+
+import javax.swing.JDialog;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.ListModel;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListDataListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import net.sourceforge.jnlp.cache.CacheUtil;
+
+import net.sourceforge.jnlp.config.DeploymentConfiguration;
+import net.sourceforge.jnlp.runtime.Translator;
+import net.sourceforge.jnlp.util.ImageResources;
+import net.sourceforge.jnlp.util.ScreenFinder;
+import net.sourceforge.swing.SwingUtils;
+
+/**
+ * This class will provide a visual way of viewing cache ids.
+ *
+ *
+ */
+public class CacheAppViewer extends JDialog {
+
+    private boolean initialized = false;
+    private static final String dialogTitle = Translator.R("CVCPDialogTitle");
+    private final DeploymentConfiguration config; // Configuration file which contains all the settings.
+
+    /**
+     * Creates a new instance of the cache viewer.
+     *
+     * @param config Deployment configuration file.
+     */
+    public CacheAppViewer(DeploymentConfiguration config) {
+        super((Frame) null, dialogTitle, true); // Don't need a parent.
+        this.setName("CacheViewer");
+        SwingUtils.info(this);
+        this.config = config;
+        if (config == null) {
+            throw new IllegalArgumentException("config: " + config);
+        }
+        setIconImages(ImageResources.INSTANCE.getApplicationImages());
+        /* Prepare for adding components to dialog box */
+        create();
+    }
+
+    private void create() {
+        Container parentPane = getContentPane();
+        Container mainPane = new JPanel();
+        parentPane.setLayout(new BorderLayout());
+        mainPane.setLayout(new GridLayout(2, 1));
+        parentPane.add(mainPane);
+        final JList<CacheUtil.CacheId> apps = new JList<>();
+        apps.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        final JButton delete = new JButton(Translator.R("TIFPDeleteFiles"));
+        delete.setEnabled(false);
+        delete.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                final Dimension d = CacheAppViewer.this.getSize();
+                SwingUtils.invokeLater(new Runnable() {
+                    @Override
+                    public void run() {
+                        CacheUtil.clearCache(apps.getSelectedValue().getId());
+                        CacheAppViewer.this.getContentPane().removeAll();
+                        CacheAppViewer.this.pack();
+                        create();
+                        CacheAppViewer.this.setSize(d);
+                    }
+                });
+            }
+        });
+        final List<CacheUtil.CacheId> content = CacheUtil.getCacheIds(".*");
+        ListModel<CacheUtil.CacheId> m = new ListModel<CacheUtil.CacheId>() {
+            @Override
+            public int getSize() {
+                return content.size();
+            }
+
+            @Override
+            public CacheUtil.CacheId getElementAt(int index) {
+                return content.get(index);
+            }
+
+            @Override
+            public void addListDataListener(ListDataListener l) {
+
+            }
+
+            @Override
+            public void removeListDataListener(ListDataListener l) {
+
+            }
+
+        };
+        apps.setModel(m);
+        final JTextArea info = new JTextArea();
+        info.setEditable(false);
+        apps.addListSelectionListener(new ListSelectionListener() {
+            @Override
+            public void valueChanged(ListSelectionEvent e) {
+                info.setText("");
+                if (apps.getSelectedValue() != null) {
+                    for (Object[] o : apps.getSelectedValue().getFiles()) {
+                        StringBuilder sb = new StringBuilder();
+                        for (int i = 0; i < o.length; i++) {
+                            Object object = o[i];
+                            if (object == null) {
+                                object = "??";
+                            }
+                            sb.append(object.toString()).append(" ;  ");
+                        }
+                        info.setText(info.getText() + sb.toString() + "\n");
+                    }
+                    delete.setEnabled(true);
+                    delete.setText(Translator.R("TIFPDeleteFiles") + " - " + apps.getSelectedValue().getFiles().size());
+                } else {
+                    delete.setEnabled(false);
+                    delete.setText(Translator.R("TIFPDeleteFiles"));
+                }
+            }
+        });
+        mainPane.add(new JScrollPane(apps));
+        mainPane.add(new JScrollPane(info));
+        parentPane.add(delete, BorderLayout.SOUTH);
+        pack();
+
+    }
+
+    public void centerDialog() {
+        ScreenFinder.centerWindowsToCurrentScreen(this);
+    }
+}
--- a/netx/net/sourceforge/jnlp/controlpanel/CachePane.java	Wed Jan 09 17:41:55 2019 +0100
+++ b/netx/net/sourceforge/jnlp/controlpanel/CachePane.java	Mon Feb 25 13:41:58 2019 +0100
@@ -31,6 +31,7 @@
 import java.awt.event.ActionListener;
 import java.awt.event.WindowEvent;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.nio.channels.FileLock;
@@ -45,10 +46,12 @@
 import javax.swing.JButton;
 import javax.swing.JComponent;
 import javax.swing.JDialog;
+import javax.swing.JFrame;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
 import javax.swing.JTable;
+import javax.swing.JTextArea;
 import javax.swing.ListSelectionModel;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
@@ -57,6 +60,7 @@
 import javax.swing.table.TableRowSorter;
 
 import net.sourceforge.jnlp.cache.CacheDirectory;
+import net.sourceforge.jnlp.cache.CacheEntry;
 import net.sourceforge.jnlp.cache.CacheUtil;
 import net.sourceforge.jnlp.cache.DirectoryNode;
 import net.sourceforge.jnlp.config.DeploymentConfiguration;
@@ -64,6 +68,7 @@
 import net.sourceforge.jnlp.runtime.Translator;
 import net.sourceforge.jnlp.util.FileUtils;
 import net.sourceforge.jnlp.util.PropertiesFile;
+import net.sourceforge.jnlp.util.StreamUtils;
 import net.sourceforge.jnlp.util.logging.OutputController;
 import net.sourceforge.jnlp.util.ui.NonEditableTableModel;
 import net.sourceforge.swing.SwingUtils;
@@ -71,18 +76,18 @@
 public class CachePane extends JPanel {
     final JDialog parent;
     final DeploymentConfiguration config;
-    private final String location;
     private JComponent defaultFocusComponent;
-    DirectoryNode root;
     String[] columns = {
             Translator.R("CVCPColName"),
             Translator.R("CVCPColPath"),
             Translator.R("CVCPColType"),
             Translator.R("CVCPColDomain"),
             Translator.R("CVCPColSize"),
-            Translator.R("CVCPColLastModified") };
+            Translator.R("CVCPColLastModified"),
+        CacheEntry.KEY_JNLP_PATH
+    };
     JTable cacheTable;
-    private JButton deleteButton, refreshButton, doneButton, cleanAll;
+    private JButton deleteButton, refreshButton, doneButton, cleanAll, infoButton;
 
     /**
      * Creates a new instance of the CachePane.
@@ -94,8 +99,6 @@
         super(new BorderLayout());
         this.parent = parent;
         this.config = config;
-        location = PathsAndFiles.CACHE_DIR.getFullPath(config);
-
         addComponents();
     }
 
@@ -117,9 +120,11 @@
                 // If no row has been selected, disable the delete button, else enable it
                 if (cacheTable.getSelectionModel().isSelectionEmpty()) {
                     deleteButton.setEnabled(false);
+                    infoButton.setEnabled(false);
                 }
                 else {
                     deleteButton.setEnabled(true);
+                    infoButton.setEnabled(true);
                 }
             }
         });
@@ -190,6 +195,34 @@
 
         List<JButton> buttons = new ArrayList<>();
 
+        this.infoButton = new JButton(Translator.R("ButMoreInformation"));
+        infoButton.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                JDialog jd = new JDialog(parent, true);
+                JTextArea t = new JTextArea();
+                t.setEditable(false);
+                
+                int row = cacheTable.getSelectedRow();
+                try {
+                    int modelRow = cacheTable.convertRowIndexToModel(row);
+                    DirectoryNode fileNode = ((DirectoryNode) cacheTable.getModel().getValueAt(modelRow, 0));
+                    File selectedFile = fileNode.getFile();
+                    File infoFile = new File(selectedFile + CacheDirectory.INFO_SUFFIX);
+                    String info = StreamUtils.readStreamAsString(new FileInputStream(infoFile), true);
+                    t.setText(info);
+                } catch (Exception ex) {
+                    t.setText(ex.toString());
+                }
+                jd.add(t);
+                jd.setSize(300, 300);
+                jd.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+                jd.setVisible(true);
+            }
+        });
+        infoButton.setEnabled(false);
+        buttons.add(infoButton);
+
         this.deleteButton = new JButton(Translator.R("CVCPButDelete"));
         deleteButton.addActionListener(new ActionListener() {
             @Override
@@ -372,6 +405,7 @@
                         cacheTable.setBackground(SystemColor.control);
                         // No data in cacheTable, so nothing to delete
                         deleteButton.setEnabled(false);
+                        infoButton.setEnabled(false);                        
                     } else {
                         cacheTable.setEnabled(true);
                         cacheTable.setBackground(SystemColor.text);
@@ -398,7 +432,7 @@
 
             NonEditableTableModel tableModel;
             (tableModel = (NonEditableTableModel)cacheTable.getModel()).setRowCount(0); //Clears the table
-            for (Object[] v : generateData(root)) {
+            for (Object[] v : generateData()) {
                 tableModel.addRow(v);
             }
         } catch (Exception exception) {
@@ -409,29 +443,35 @@
         }
     }
 
+   
     /**
      * This creates the data for the table.
-     * 
-     * @param root The location of cache data.
-     * @return ArrayList containing an Object array of data for each row in the table.
+     *
+     * @return ArrayList containing an Object array of data for each row in the
+     * table.
      */
-    private ArrayList<Object[]> generateData(DirectoryNode root) {
-        root = new DirectoryNode("Root", location, null);
+    public static ArrayList<Object[]> generateData() {
+        DirectoryNode root = new DirectoryNode("Root", PathsAndFiles.CACHE_DIR.getFile(), null);
         CacheDirectory.getDirStructure(root);
         ArrayList<Object[]> data = new ArrayList<>();
 
         for (DirectoryNode identifier : root.getChildren()) {
             for (DirectoryNode type : identifier.getChildren()) {
                 for (DirectoryNode domain : type.getChildren()) {
+                    //after domain, there is optional port dir. It is skipped here (as is skipped path on domain)
                     for (DirectoryNode leaf : CacheDirectory.getLeafData(domain)) {
                         final File f = leaf.getFile();
+                        PropertiesFile pf = new PropertiesFile(new File(f.toString() + CacheDirectory.INFO_SUFFIX));
+                        // if jnlp-path in .info equals path of app to delete mark to delete
+                        String jnlpPath = pf.getProperty(CacheEntry.KEY_JNLP_PATH);
                         Object[] o = {
                             leaf,
                             f.getParentFile(),
                             type,
                             domain,
                             f.length(),
-                            new Date(f.lastModified())
+                            new Date(f.lastModified()),
+                            jnlpPath
                         };
                         data.add(o);
                     }
@@ -456,6 +496,7 @@
         parent.getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
         // Disable dialog and buttons while operating
         deleteButton.setEnabled(false);
+        infoButton.setEnabled(false);
         refreshButton.setEnabled(false);
         doneButton.setEnabled(false);
         cleanAll.setEnabled(false);
@@ -466,6 +507,7 @@
         // If nothing selected then keep deleteButton disabled
         if (!cacheTable.getSelectionModel().isSelectionEmpty()) {
             deleteButton.setEnabled(true);
+            infoButton.setEnabled(true);
         }
         // Enable buttons
         refreshButton.setEnabled(true);
--- a/netx/net/sourceforge/jnlp/controlpanel/TemporaryInternetFilesPanel.java	Wed Jan 09 17:41:55 2019 +0100
+++ b/netx/net/sourceforge/jnlp/controlpanel/TemporaryInternetFilesPanel.java	Mon Feb 25 13:41:58 2019 +0100
@@ -82,6 +82,7 @@
     private final JLabel lCompression;
     private final JLabel lCacheSize;
     private final JButton bViewFiles;
+    private final JButton bCleanByApp;
     private final JPanel diskSpacePanel;
 
     public TemporaryInternetFilesPanel(final DeploymentConfiguration config) {
@@ -111,6 +112,7 @@
         location = new JTextField(PathsAndFiles.CACHE_DIR.getFullPath(config));
         locationDescription = new JLabel(Translator.R("TIFPLocationLabel") + ":");
         bViewFiles = new JButton(Translator.R("TIFPViewFiles"));
+        bCleanByApp = new JButton(Translator.R("TIFPCleanByApp"));
 
         diskSpacePanel = new JPanel();
         diskSpacePanel.setLayout(new GridBagLayout());
@@ -235,6 +237,18 @@
                 CacheViewer.showCacheDialog(config);
             }
         });
+        
+         bCleanByApp.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                 CacheAppViewer psd = new CacheAppViewer(config);
+                 psd.setResizable(true);
+                 psd.centerDialog();
+                 psd.setVisible(true);
+                 psd.dispose();
+             }
+        });
+
 
         c.gridy = 4;
         c.gridx = 0;
@@ -250,8 +264,11 @@
         c.gridx = 2;
         c.weightx = 0.5;
         diskSpacePanel.add(bLocation, c);
-        c.gridx = 3;
+        c.gridy = 6;
+        c.gridx = 1;
         diskSpacePanel.add(bViewFiles, c);
+        c.gridx = 2;
+        diskSpacePanel.add(bCleanByApp, c);
 
         JPanel panel = new JPanel();
         panel.setLayout(new BorderLayout());
--- a/netx/net/sourceforge/jnlp/resources/Messages.properties	Wed Jan 09 17:41:55 2019 +0100
+++ b/netx/net/sourceforge/jnlp/resources/Messages.properties	Mon Feb 25 13:41:58 2019 +0100
@@ -344,7 +344,12 @@
 BOXml       = Uses a strict XML parser to parse the JNLP file.
 BOredirect  = Follows HTTP redirects.
 BXnofork    = Do not create another JVM.
-BXclearcache= Clean the JNLP application cache.
+BXclearcache= Clean the JNLP application cache. If you pass argument, only specified application is deleted.
+BXSingleCacheCleared=Clearing cache for: {0}
+BXSingleCacheClearNotFound=No ID matching {0} found!
+BXSingleCacheMoreThenOneId=More then one ID is matching {0}!
+BXSingleCacheFileCount=Alerting: {0} of files
+BXcacheids  = List available IDs in cache, which you can use to delete individual applications.
 BXignoreheaders= Skip jar header verification.
 BXoffline   = Prevent ITW network connection. Only cache will be used. Application can still connect.
 BOHelp1     = Prints out information about supported command and basic usage.
@@ -378,6 +383,7 @@
 NOAnone=No argument expected
 NOAone=Exactly one argument expected
 NOAonemore=Expected one or more arguments
+NOAnonorone=Expected none or one argument
 NOAevennumber=Expected even number of arguments with param=value as valid argument
 
 # Allowed man sections
@@ -405,17 +411,17 @@
 CFakedCache=Cache was corrupt and has been fixed. It is strongly recommended that you run ''javaws -Xclearcache'' and rerun your application as soon as possible. You can also use via itw-settings Cache -> View files -> Purge
 
 # extended access warning pane 
-EXAWdesktopWants=Desktop icon shortcut (applications want to).
-EXAWdesktopDontWants=Desktop icon shortcut (applications don''t want to, but you still can).
-EXAWsubmenu=Menu icon shortcut (applications will try to include to submenu - {0}).
-EXAWmenuWants=Menu icon shortcut (applications want to).
-EXAWmenuDontWants=Menu icon shortcut (applications don''t want to, but you still can).
+EXAWdesktopWants=Desktop shortcut (Application has requested)
+EXAWdesktopDontWants=Desktop shortcut (Application didn't request).
+EXAWsubmenu=Menu shortcut (Application will try to include in Submenu - {0}).
+EXAWmenuWants=Menu shortcut (Application has requested).
+EXAWmenuDontWants=Menu shortcut (Application didn't request).
 EXAWsettingsInfo=Your current setting is: {0}. You can change it in itweb-settings in {1} panel.
 EXAWsettingsManage=You can manage existing menu entries in itweb-settings in {0} panel.
-EXAWrememberByApp=Remember by application
-EXAWrememberByPage=Remember by domain
+EXAWrememberByApp=Remember by Application
+EXAWrememberByPage=Remember by Domain
 EXAWdontRemember=Don''t remember
-EXAWrememberByAppTooltip=<html>This application will never ask for more permissions questions</html>
+EXAWrememberByAppTooltip=<html>This application will never ask for more permissions</html>
 EXAWrememberByPageTooltip=<html>All applications from this domain will stop asking, and will follow your current decision on all permissions</html>
 EXAWdontRememberTooltip=<html>Your decision will be used only for this single permission for this single run</html>
 EXAWbrowser=browser desktop item
@@ -426,7 +432,7 @@
 EXAWbrowserTolltip=<html>Browser shortcut<br> \
 <li>This option will create shortcut to open your browser with current page loaded</li> \
 <li>If your browser support offline run, this is the safest option</li></html>
-EXAWbrowsersTolltip=<html>browser used for lunching this applet (will launch IcedTea-web later)<br> \
+EXAWbrowsersTolltip=<html>browser used for launching this applet (will launch IcedTea-web later)<br> \
 <li>The default browser was preset</li> \
 <li>Feel free to include a browser of your choice</li></html>
 EXAWgeneratedTolltip=<html><li>The jnlp file will be generated from your current html page</li> \
@@ -446,7 +452,9 @@
 # Security
 SFileReadAccess=The application has requested read access to {0}. Do you want to allow this action?
 SFileWriteAccess=The application has requested write access to {0}. Do you want to allow this action?
-SDesktopShortcut=The application has requested permission to create a desktop and/or menu launcher. Do you want to allow this action?
+SDesktopShortcut=The Application has requested permission to create Desktop and Menu shortcuts. Do you want to allow this action?
+WinDesktopError=Failed to create windows desktop icons. Most likely, your icedtea-web was built without mslinks.jar or mslinks.jar is not to be found. \
+Try to fix it. If it do not help, maybe the application itself is to be blamed. At the end, study the exceptions above carefully and contact icedtea-web team.
 SSigUnverified=The application''s digital signature cannot be verified. Do you want to run the application? It will be granted unrestricted access to your computer.
 SSigVerified=The application''s digital signature has been verified. Do you want to run the application? It will be granted unrestricted access to your computer.
 SSignatureError=The application''s digital signature has an error. Do you want to run the application? It will be granted unrestricted access to your computer.
@@ -1023,7 +1031,8 @@
 TIFPMax=Max
 TIFPCacheSize=Set the amount of disk space for storing temporary files (MB)
 TIFPDeleteFiles=Delete files
-TIFPViewFiles=View files...
+TIFPViewFiles=View files
+TIFPCleanByApp=Clean by app
 TIFPFileChooserChooseButton=Choose
 TIFPLimitCacheSize=Limit cache size
 TIFPCacheSizeSpinnerValueTooLargeWarning=<html><b>WARNING:</b> Uses more space than {0} MB available</html>
--- a/netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java	Wed Jan 09 17:41:55 2019 +0100
+++ b/netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java	Mon Feb 25 13:41:58 2019 +0100
@@ -18,6 +18,9 @@
 
 import java.awt.Window;
 import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.net.URL;
 import java.security.AccessControlContext;
 import java.security.AccessController;
@@ -28,7 +31,6 @@
 import javax.swing.event.EventListenerList;
 
 import sun.awt.AppContext;
-
 import net.sourceforge.jnlp.JNLPFile;
 import net.sourceforge.jnlp.PropertyDesc;
 import net.sourceforge.jnlp.SecurityDesc;
@@ -39,6 +41,7 @@
 import net.sourceforge.jnlp.security.SecurityDialogs;
 import net.sourceforge.jnlp.security.SecurityDialogs.AccessType;
 import net.sourceforge.jnlp.security.dialogresults.AccessWarningPaneComplexReturn;
+import net.sourceforge.jnlp.util.GenericDesktopEntry;
 import net.sourceforge.jnlp.util.logging.OutputController;
 import net.sourceforge.jnlp.util.WeakList;
 import net.sourceforge.jnlp.util.XDesktopEntry;
@@ -150,47 +153,99 @@
      */
 
     private void addMenuAndDesktopEntries() {
-        XDesktopEntry entry = new XDesktopEntry(file);
         ShortcutDesc sd = file.getInformation().getShortcut();
-        File possibleDesktopFile = entry.getDesktopIconFile();
-        File possibleMenuFile = entry.getLinuxMenuIconFile();
-        File generatedJnlp = entry.getGeneratedJnlpFileName();
-        //if one of menu or desktop exists, do not bother user
-        boolean exists = false;
-        if (possibleDesktopFile.exists()) {
-            OutputController.getLogger().log("ApplicationInstance.addMenuAndDesktopEntries(): file - "
-                    + possibleDesktopFile.getAbsolutePath() + " already exists. Refreshing and not proceeding with desktop additions");
-            exists = true;
-            if (JNLPRuntime.isOnline()) {
-                entry.refreshExistingShortcuts(false, true); //update
+        if (JNLPRuntime.isWindows()) {
+            OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, "Generating windows desktop shorcut");
+            try {
+                Object instance = null;
+                try {
+                    Class cl = Class.forName("net.sourceforge.jnlp.util.WindowsDesktopEntry");
+                    Constructor cons = cl.getConstructor(JNLPFile.class);
+                    instance = cons.newInstance(file);
+                    //catch both, for case that mslink was removed after build
+                } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException | NoClassDefFoundError | InstantiationException e) {
+                    OutputController.getLogger().log(OutputController.Level.WARNING_ALL, e);
+                }
+                GenericDesktopEntry wde = (GenericDesktopEntry) instance;
+                if (!wde.getDesktopIconFile().exists()) {
+                    // if the desktop shortcut doesn't exist ask
+                    AccessWarningPaneComplexReturn ics = getComplexReturn(sd);
+                    if (ics != null && ics.toBoolean()) {
+                        boolean isDesktop = false;
+                        if (ics.getDekstop() != null && ics.getDekstop().isCreate()) {
+                            isDesktop = true;
+                        }
+                        boolean isMenu = false;
+                        if (ics.getMenu() != null && ics.getMenu().isCreate()) {
+                            isMenu = true;
+                        }
+                        // if setting is always create theen ics will be true but "get" properties will be null, so set to create
+                        if (ics.getDekstop() == null && ics.toBoolean()) {
+                            isDesktop = true;
+                        };
+                        if (ics.getMenu() == null && ics.toBoolean()) {
+                            isMenu = true;
+                        };
+                        // create shortcuts if its ok
+                        if (isDesktop) {
+                            wde.createShortcutOnWindowsDesktop();
+                        }
+                        if (isMenu) {
+                            wde.createWindowsMenu();
+                        }
+                    }
+                } else {
+                    // refresh shortcut if it already exists
+                    wde.createShortcutOnWindowsDesktop();
+                    wde.createWindowsMenu();
+                }
+            } catch (Throwable ex) {
+                OutputController.getLogger().log(OutputController.Level.WARNING_ALL, ex);
+                String message = Translator.R("WinDesktopError");
+                OutputController.getLogger().log(OutputController.Level.WARNING_ALL, message);
             }
+        } else {
+            // do non-windows desktop stuff
+            GenericDesktopEntry entry = new XDesktopEntry(file);
+            File possibleDesktopFile = entry.getDesktopIconFile();
+            File possibleMenuFile = entry.getLinuxMenuIconFile();
+            File generatedJnlp = entry.getGeneratedJnlpFileName();
+	        //if one of menu or desktop exists, do not bother user
+	        boolean exists = false;
+	        if (possibleDesktopFile.exists()) {
+	            OutputController.getLogger().log("ApplicationInstance.addMenuAndDesktopEntries(): file - "
+	                    + possibleDesktopFile.getAbsolutePath() + " already exists. Refreshing and not proceeding with desktop additions");
+	            exists = true;
+	            if (JNLPRuntime.isOnline()) {
+	                entry.refreshExistingShortcuts(false, true); //update
+	            }
+	        }
+	        if (possibleMenuFile.exists()) {
+	            OutputController.getLogger().log("ApplicationInstance.addMenuAndDesktopEntries(): file - "
+	                    + possibleMenuFile.getAbsolutePath() + " already exists. Refreshing and not proceeding with desktop additions");
+	            exists = true;
+	            if (JNLPRuntime.isOnline()) {
+	                entry.refreshExistingShortcuts(true, false); //update
+	            }
+	        }
+	        if (generatedJnlp.exists()) {
+	            OutputController.getLogger().log("ApplicationInstance.addMenuAndDesktopEntries(): generated file - "
+	                    + generatedJnlp.getAbsolutePath() + " already exists. Refreshing and not proceeding with desktop additions");
+	            exists = true;
+	            if (JNLPRuntime.isOnline()) {
+	                entry.refreshExistingShortcuts(true, true); //update
+	            }
+	        }
+	        if (exists){
+	            return;
+	        }
+	        AccessWarningPaneComplexReturn ics = getComplexReturn(sd);
+	        if (ics !=null && ics.toBoolean()) {
+	            entry.createDesktopShortcuts(ics.getMenu(), ics.getDekstop(), isSigned());
+	        }
         }
-        if (possibleMenuFile.exists()) {
-            OutputController.getLogger().log("ApplicationInstance.addMenuAndDesktopEntries(): file - "
-                    + possibleMenuFile.getAbsolutePath() + " already exists. Refreshing and not proceeding with desktop additions");
-            exists = true;
-            if (JNLPRuntime.isOnline()) {
-                entry.refreshExistingShortcuts(true, false); //update
-            }
-        }
-        if (generatedJnlp.exists()) {
-            OutputController.getLogger().log("ApplicationInstance.addMenuAndDesktopEntries(): generated file - "
-                    + generatedJnlp.getAbsolutePath() + " already exists. Refreshing and not proceeding with desktop additions");
-            exists = true;
-            if (JNLPRuntime.isOnline()) {
-                entry.refreshExistingShortcuts(true, true); //update
-            }
-        }
-        if (exists){
-            return;
-        }
-        AccessWarningPaneComplexReturn ics = getComplexReturn(sd);
-        if (ics !=null && ics.toBoolean()) {
-            entry.createDesktopShortcuts(ics.getMenu(), ics.getDekstop(), isSigned());
-        }
-
     }
-
+ 	   
     /**
      * Indicates whether a desktop launcher/shortcut should be created for this
      * application instance
--- a/netx/net/sourceforge/jnlp/runtime/Boot.java	Wed Jan 09 17:41:55 2019 +0100
+++ b/netx/net/sourceforge/jnlp/runtime/Boot.java	Mon Feb 25 13:41:58 2019 +0100
@@ -90,6 +90,12 @@
 
     private static OptionParser optionParser;
 
+    public static OptionParser getOptionParser() {
+        return optionParser;
+    }
+    
+    
+
     /**
      * Launch the JNLP file specified by the command-line arguments.
      *
@@ -342,6 +348,18 @@
         JNLPRuntime.setOfflineForced(optionParser.hasOption(OptionsDefinitions.OPTIONS.OFFLINE));
         JNLPRuntime.initialize(true);
 
+        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.LISTCACHEIDS)) {
+            List<String> optionArgs = optionParser.getMainArgs();
+            if (optionArgs.size() > 0) {
+                //clear one app 
+                CacheUtil.listCacheIds(optionArgs.get(0));
+            } else {
+                // clear all cache
+                CacheUtil.listCacheIds(".*");
+            }
+            return null;
+        }
+
         /*
          * FIXME
          * This should have been done with the rest of the argument parsing
@@ -349,7 +367,14 @@
          * and baseDir is initialized here
          */
         if (optionParser.hasOption(OptionsDefinitions.OPTIONS.CLEARCACHE)) {
-            CacheUtil.clearCache();
+            List<String> optionArgs = optionParser.getMainArgs();
+            if (optionArgs.size() > 0) {
+                //clear one app 
+                CacheUtil.clearCache(optionArgs.get(0));
+            } else {
+                // clear all cache
+                CacheUtil.clearCache();
+            }
             return null;
         }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/netx/net/sourceforge/jnlp/util/GenericDesktopEntry.java	Mon Feb 25 13:41:58 2019 +0100
@@ -0,0 +1,51 @@
+/* 
+Copyright (C) 2013 Red Hat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package net.sourceforge.jnlp.util;
+
+import java.io.File;
+import java.io.IOException;
+import net.sourceforge.jnlp.security.dialogresults.AccessWarningPaneComplexReturn;
+
+/**
+ *
+ * Thsi is very wierd interface, as two implementing classes have empty
+ * intersection. The interface exists only because windows implementation depnds
+ * on mslink.jar, and thus is optional. todo. unify the X and win
+ * implementations so this interface have sense
+ */
+public interface GenericDesktopEntry {
+
+    //linux
+    public void createDesktopShortcuts(AccessWarningPaneComplexReturn.ShortcutResult menu, AccessWarningPaneComplexReturn.ShortcutResult desktop, boolean isSigned);
+
+    public void refreshExistingShortcuts(boolean desktop, boolean menu);
+
+    public File getGeneratedJnlpFileName();
+
+    public File getLinuxMenuIconFile();
+
+    //windows
+    public void createShortcutOnWindowsDesktop() throws IOException;
+
+    public void createWindowsMenu() throws IOException;
+
+    //shared!
+    public String getDesktopIconFileName();
+
+    public File getDesktopIconFile();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/netx/net/sourceforge/jnlp/util/WindowsDesktopEntry.java	Mon Feb 25 13:41:58 2019 +0100
@@ -0,0 +1,162 @@
+// Copyright (C) 2009 Red Hat, Inc.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+package net.sourceforge.jnlp.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
+import java.util.Iterator;
+import java.util.List;
+import mslinks.ShellLink;
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.cache.CacheLRUWrapper;
+import net.sourceforge.jnlp.security.dialogresults.AccessWarningPaneComplexReturn;
+import net.sourceforge.jnlp.util.logging.OutputController;
+
+/**
+ * Based on https://github.com/DmitriiShamrikov/mslinkshttps://github.com/DmitriiShamrikov/mslinks
+ */
+public class WindowsDesktopEntry implements GenericDesktopEntry {
+
+    private final JNLPFile file;
+
+    public WindowsDesktopEntry(JNLPFile file) {
+        this.file = file;
+    }
+
+    @Override
+    public String getDesktopIconFileName() {
+        return XDesktopEntry.getDesktopIconName(file) + ".lnk";
+    }
+
+    private String getDesktopLnkPath() {
+        return System.getenv("userprofile") + "/Desktop/" + getDesktopIconFileName();
+    }
+
+    @Override
+    public File getDesktopIconFile() {
+        return new File(getDesktopLnkPath());
+    }
+
+    @Override
+    public void createShortcutOnWindowsDesktop() throws IOException {
+        String path = getDesktopLnkPath();
+        String JavaWsBin = XDesktopEntry.getJavaWsBin();
+        String favIcon = getFavIcon();
+        ShellLink sl = ShellLink.createLink(JavaWsBin).setCMDArgs(file.getSourceLocation().toString());
+        if (favIcon != null) {
+            favIcon = favIcon.substring(6);
+            sl.setIconLocation(favIcon);
+        }
+        sl.saveTo(path);
+        // write shortcut path to list
+        manageShortcutList(ManageMode.A, path);
+    }
+
+    @Override
+    public void createWindowsMenu() throws IOException {
+        // create menu item
+        // see if menu is defined in jnlp, else don't do it
+        String path = file.getInformation().getShortcut().getMenu().getSubMenu();
+        if (path != null) {
+            path = System.getenv("userprofile") + "/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/" + path;
+            // check to see if menu dir exists and create if not
+            File menuDir = new File(path);
+            if (!menuDir.exists()) {
+                menuDir.mkdir();
+            }
+            String JavaWsBin = XDesktopEntry.getJavaWsBin();
+            String favIcon = getFavIcon();
+            ShellLink sl = ShellLink.createLink(JavaWsBin).setCMDArgs(file.getSourceLocation().toString());
+            // setup uninstall shortcut
+            ShellLink ul = ShellLink.createLink(JavaWsBin).setCMDArgs("-Xclearcache " + file.getFileLocation().toString());
+            if (favIcon != null) {
+                favIcon = favIcon.substring(6);
+                sl.setIconLocation(favIcon);
+                ul.setIconLocation(favIcon);
+            }
+            sl.saveTo(path + "/" + file.getInformation().getTitle()+ ".lnk");
+            ul.saveTo(path + "/Uninstall " + file.getInformation().getTitle() + ".lnk");
+            // write shortcuts to list
+            manageShortcutList(ManageMode.A, path + "/" + file.getInformation().getTitle() + ".lnk");
+            manageShortcutList(ManageMode.A, path + "/Uninstall " + file.getInformation().getTitle() + ".lnk");
+        }
+    }
+
+    private void manageShortcutList(ManageMode mode, String path) throws IOException {
+        if (!CacheLRUWrapper.getInstance().getWindowsShortcutList().exists()) {
+            CacheLRUWrapper.getInstance().getWindowsShortcutList().createNewFile();
+        }
+
+        if (ManageMode.A == mode) {
+            List<String> lines = Files.readAllLines(CacheLRUWrapper.getInstance().getWindowsShortcutList().toPath(), Charset.forName("UTF-8"));
+            Iterator it = lines.iterator();
+            String sItem = "";
+            String sPath;
+            Boolean fAdd = true;
+            // check to see if line exists, if not add it
+            while (it.hasNext()) {
+                sItem = it.next().toString();
+                String[] sArray = sItem.split(",");
+                String application = sArray[0]; //??
+                sPath = sArray[1];
+                if (sPath.equalsIgnoreCase(path)) {
+                    // it exists don't add
+                    fAdd = false;
+                    break;
+                }
+            }
+            if (fAdd) {
+                OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, "Adding sCut to list = " + sItem);
+                String scInfo = file.getFileLocation().toString() + ",";
+                scInfo += path + "\r\n";
+                Files.write(CacheLRUWrapper.getInstance().getWindowsShortcutList().toPath(), scInfo.getBytes(), StandardOpenOption.APPEND);
+            }
+        }
+    }
+
+    private String getFavIcon() {
+        return XDesktopEntry.getFavIcon(file);
+    }
+
+    @Override
+    public void createDesktopShortcuts(AccessWarningPaneComplexReturn.ShortcutResult menu, AccessWarningPaneComplexReturn.ShortcutResult desktop, boolean isSigned) {
+        throw new UnsupportedOperationException("not suported on windows like systems");
+    }
+
+    @Override
+    public void refreshExistingShortcuts(boolean desktop, boolean menu) {
+        throw new UnsupportedOperationException("not suported on windows like systems");
+    }
+
+    @Override
+    public File getGeneratedJnlpFileName() {
+        throw new UnsupportedOperationException("not suported on windows like systems");
+    }
+
+    @Override
+    public File getLinuxMenuIconFile() {
+        throw new UnsupportedOperationException("not suported on windows like systems");
+    }
+
+    private static enum ManageMode {
+        //appned?
+        A
+    }
+
+}
--- a/netx/net/sourceforge/jnlp/util/XDesktopEntry.java	Wed Jan 09 17:41:55 2019 +0100
+++ b/netx/net/sourceforge/jnlp/util/XDesktopEntry.java	Mon Feb 25 13:41:58 2019 +0100
@@ -26,6 +26,7 @@
 import java.io.OutputStreamWriter;
 import java.io.Reader;
 import java.io.StringReader;
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
@@ -37,10 +38,10 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import javax.swing.filechooser.FileSystemView;
 
 import net.sourceforge.jnlp.IconDesc;
 import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.Launcher;
 import net.sourceforge.jnlp.OptionsDefinitions;
 import net.sourceforge.jnlp.PluginBridge;
 import net.sourceforge.jnlp.cache.CacheUtil;
@@ -76,8 +77,9 @@
  
  * @author (not so proudly) Jiri Vanek
  */
-public class XDesktopEntry {
 
+public class XDesktopEntry implements GenericDesktopEntry {
+ 
     public static final String JAVA_ICON_NAME = "javaws";
 
     private JNLPFile file = null;
@@ -110,7 +112,7 @@
      * @param isSigned whether the app is signed
      * @return reader with desktop shortcut specification
      */
-    public Reader getContentsAsReader(boolean menu, AccessWarningPaneComplexReturn.ShortcutResult info, boolean isSigned) {
+     Reader getContentsAsReader(boolean menu, AccessWarningPaneComplexReturn.ShortcutResult info, boolean isSigned) {
 
         File generatedJnlp = null;
         if (file instanceof PluginBridge && (info.getShortcutType() == AccessWarningPaneComplexReturn.ShortcutResult.Shortcut.GENERATED_JNLP || info.getShortcutType() == AccessWarningPaneComplexReturn.ShortcutResult.Shortcut.JNLP_HREF)) {
@@ -200,10 +202,10 @@
         
     }
     
-    private String getJavaWsBin() {
+    public static String getJavaWsBin() {
         //Shortcut executes the jnlp as it was with system preferred java. It should work fine offline
         //absolute - works in case of self built
-        String exec = System.getProperty("icedtea-web.bin.location");
+        String exec = System.getProperty(Launcher.KEY_JAVAWS_LOCATION);
         String pathResult = findOnPath(new String[]{"javaws", System.getProperty("icedtea-web.bin.name")});
         if (pathResult != null) {
             return pathResult;
@@ -267,11 +269,11 @@
     /**
      * @return the size of the icon (in pixels) for the desktop shortcut
      */
-    public int getIconSize() {
+    private int getIconSize() {
         return iconSize;
     }
 
-    public File getShortcutTmpFile() {
+    File getShortcutTmpFile() {
         String userTmp = PathsAndFiles.TMP_DIR.getFullPath();
         File shortcutFile = new File(userTmp + File.separator + getDesktopIconFileName());
         return shortcutFile;
@@ -283,7 +285,7 @@
      * @param size the size (in pixels) of the icon to use. Commonly used sizes
      *        are of 16, 22, 32, 48, 64 and 128
      */
-    public void setIconSize(int size) {
+    private void setIconSize(int size) {
         iconSize = size;
     }
 
@@ -293,6 +295,7 @@
      * @param desktop how to create on desktop
      * @param isSigned if it is signed
      */
+    @Override
     public void createDesktopShortcuts(AccessWarningPaneComplexReturn.ShortcutResult menu, AccessWarningPaneComplexReturn.ShortcutResult desktop, boolean isSigned) {
         boolean isDesktop = false;
         if (desktop != null && desktop.isCreate()) {
@@ -398,6 +401,7 @@
     }
 
 
+    @Override
     public void refreshExistingShortcuts(boolean desktop, boolean menu) {
         //TODO TODO TODO TODO TODO TODO TODO TODO 
         //check existing jnlp files
@@ -408,6 +412,7 @@
 
     }
 
+    @Override
     public File getGeneratedJnlpFileName() {
         String name = FileUtils.sanitizeFileName(file.createJnlpTitle());
         while (name.endsWith(".jnlp")) {
@@ -450,16 +455,7 @@
                 cantCache();
             }
         } else {
-            //try favicon.ico
-            try {
-                URL favico = new URL(
-                        file.getCodeBase().getProtocol(),
-                        file.getCodeBase().getHost(),
-                        file.getCodeBase().getPort(),
-                        "/" + FAVICON);
-                JNLPFile.openURL(favico, null, UpdatePolicy.ALWAYS);
-                //this MAY throw npe, if url (specified in jnlp) points to 404
-                URL urlLocation = CacheUtil.getCachedResourceURL(favico, null, UpdatePolicy.SESSION);
+                URL urlLocation = getFavIconUrl(file);
                 if (urlLocation == null) {
                     cantCache();
                 }
@@ -467,10 +463,7 @@
                 if (!location.startsWith("file:")) {
                     cantCache();
                 }
-            } catch (IOException ex) {
-                //favicon 404 or similar
-                OutputController.getLogger().log(ex);
-            }
+
         }
         if (location != null) {
             String origLocation = location.substring("file:".length());
@@ -482,7 +475,7 @@
             File source = new File(origLocation);
             String targetName = source.getName();
             if (targetName.equals(FAVICON)) {
-                targetName = file.getCodeBase().getHost() + ".ico";
+                targetName = file.getNotNullProbalbeCodeBase().getHost() + ".ico";
             }
             File target = new File(PathsAndFiles.ICONS_DIR.getFile(), targetName);
             Files.copy(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
@@ -490,32 +483,99 @@
             OutputController.getLogger().log(OutputController.Level.ERROR_DEBUG, "Cached desktop shortcut icon: " + target + " ,  With source from: " + origLocation);
         }
     }
+    
+    static String getFavIcon(JNLPFile file) {
+        URL u = getFavIconUrl(file);
+        if (u == null){
+            return null;
+        } else {
+            return u.toString();
+        }
+    }
+    static List<String> possibleFavIconLocations(String path) {
+        while (path.endsWith("/") || path.endsWith("\\")) {
+            path = path.substring(0, path.length() - 1);
+        }
+        if (!path.startsWith("/")) {
+            path = "/" + path;
+        }
+        List<String> r = new ArrayList<>();
+        do {
+            r.add(path);
+            int last = Math.max(path.lastIndexOf("\\"), path.lastIndexOf("/"));
+            if (last >= 0) {
+                path = path.substring(0, last);
+            }
+        } while (path.contains("/") || path.contains("\\"));
+        if (!r.contains("")) {
+            r.add("");
+        }
+        return r;
+    }
+    
+    private static URL favUrl(String delimiter, String path, JNLPFile file) throws MalformedURLException {
+        return new URL(
+                file.getNotNullProbalbeCodeBase().getProtocol(),
+                file.getNotNullProbalbeCodeBase().getHost(),
+                file.getNotNullProbalbeCodeBase().getPort(),
+                path + delimiter + FAVICON);
+    }
+
+    private static URL getFavIconUrl(JNLPFile file) {
+        try {
+            for (String path : possibleFavIconLocations(file.getNotNullProbalbeCodeBase().getPath())) {
+                URL favico = favUrl("/", path, file);
+                //JNLPFile.openURL(favico, null, UpdatePolicy.ALWAYS);
+                //this MAY throw npe, if url (specified in jnlp) points to 404
+                //the below works just fine
+                URL urlLocation = CacheUtil.getCachedResourceURL(favico, null, UpdatePolicy.SESSION);
+                if (urlLocation != null) {
+                    return urlLocation;
+                }
+            }
+            //the icon is much more likely to be found behind / then behinf \/ 
+            //So rather duplicating the code here, then wait double time if the icon will be at the start of the path
+            for (String path : possibleFavIconLocations(file.getNotNullProbalbeCodeBase().getPath())) {
+                URL favico = favUrl("\\", path, file);
+                URL urlLocation = CacheUtil.getCachedResourceURL(favico, null, UpdatePolicy.SESSION);
+                if (urlLocation != null) {
+                    return urlLocation;
+                }
+            }
+        } catch (Exception ex) {
+            //favicon 404 or similar
+            OutputController.getLogger().log(ex);
+        }
+        return null;
+    }
 
     private void cantCache() throws NonFileProtocolException {
         throw new NonFileProtocolException("Unable to cache icon");
     }
 
     private String getDesktopIconName() {
+        return getDesktopIconName(file);
+    }
+    
+    static String getDesktopIconName(JNLPFile file) {
         return sanitize(file.createJnlpTitle());
     }
 
     public File getDesktopIconFile() {
             return new File(getDesktop(), getDesktopIconFileName());
     }
-    public static File getDesktop(){
-        if (JNLPRuntime.isWindows()) {
-            FileSystemView filesys = FileSystemView.getFileSystemView();
-            return filesys.getHomeDirectory();
-        } else {
-            return new File(findFreedesktopOrgDesktopPathCatch());
-        }
+    
+    static File getDesktop() {
+        return new File(findFreedesktopOrgDesktopPathCatch());
     }
 
+    @Override
     public File getLinuxMenuIconFile() {
         return new File(findAndVerifyJavawsMenuDir() + "/" + getDesktopIconFileName());
     }
     
-    private String getDesktopIconFileName() {
+    @Override
+    public String getDesktopIconFileName() {
         return getDesktopIconName() + ".desktop";
     }
 
@@ -623,4 +683,14 @@
         }
 
     }
+    
+     @Override
+    public void createShortcutOnWindowsDesktop() throws IOException {
+        throw new UnsupportedOperationException("not suported on linux like systems");
+    }
+
+    @Override
+    public void createWindowsMenu() throws IOException {
+        throw new UnsupportedOperationException("not suported on linux like systems");
+    }
 }
--- a/netx/net/sourceforge/jnlp/util/optionparser/OptionParser.java	Wed Jan 09 17:41:55 2019 +0100
+++ b/netx/net/sourceforge/jnlp/util/optionparser/OptionParser.java	Mon Feb 25 13:41:58 2019 +0100
@@ -41,7 +41,6 @@
 import java.util.List;
 
 import net.sourceforge.jnlp.OptionsDefinitions;
-import net.sourceforge.jnlp.util.logging.OutputController;
 
 import static net.sourceforge.jnlp.runtime.Translator.R;
 
@@ -102,7 +101,7 @@
     }
 
     private boolean isOneArgumentNotFull(final ParsedOption lastOption) {
-        return lastOption.getOption().hasOneArgument() && lastOption.getParams().size() == 0;
+        return lastOption.getOption().hasOneArgument() && lastOption.getParams().isEmpty();
     }
 
     private boolean oneOrMoreArguments(final ParsedOption lastOption) {
@@ -143,10 +142,7 @@
     }
 
     private boolean isOption(String input) {
-        if (argumentToOption(input) != null) {
-            return true;
-        }
-        return false;
+        return argumentToOption(input) != null;
     }
 
     protected static boolean stringEqualsOption(String input, OptionsDefinitions.OPTIONS opt) {
@@ -169,10 +165,7 @@
     }
 
     public boolean mainArgExists() {
-        if (mainArgumentList.size() > 0) {
-            return true;
-        }
-        return false;
+        return mainArgumentList.size() > 0;
     }
 
     public String getMainArg() {
--- a/tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java	Wed Jan 09 17:41:55 2019 +0100
+++ b/tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java	Mon Feb 25 13:41:58 2019 +0100
@@ -104,4 +104,37 @@
         final File expected = new File("/tmp/https/example.com/applet/applet.php");
         Assert.assertEquals(expected, CacheUtil.urlToPath(u, "/tmp"));
     }
+    
+    @Test
+    public void CacheID(){
+        CacheUtil.CacheId cj11 = new CacheUtil.CacheJnlpId("a");
+        CacheUtil.CacheId cj12 = new CacheUtil.CacheJnlpId("a");
+        CacheUtil.CacheId cj2 = new CacheUtil.CacheJnlpId("b");
+        CacheUtil.CacheId cj31 = new CacheUtil.CacheJnlpId(null);
+        CacheUtil.CacheId cj32 = new CacheUtil.CacheJnlpId(null);
+        CacheUtil.CacheId cd11 = new CacheUtil.CacheDomainId("a");
+        CacheUtil.CacheId cd12 = new CacheUtil.CacheDomainId("a");
+        CacheUtil.CacheId cd2 = new CacheUtil.CacheDomainId("b");
+        CacheUtil.CacheId cd31 = new CacheUtil.CacheDomainId(null);
+        CacheUtil.CacheId cd32 = new CacheUtil.CacheDomainId(null);
+        
+        Assert.assertEquals(cj11, cj11);
+        Assert.assertEquals(cj11, cj12);
+        Assert.assertEquals(cd11, cd11);
+        Assert.assertEquals(cd11, cd12);
+        Assert.assertEquals(cj31, cj31);
+        Assert.assertEquals(cj31, cj32);
+        Assert.assertEquals(cd31, cd31);
+        Assert.assertEquals(cd31, cd32);
+        
+        Assert.assertNotEquals(cj11, cj2);
+        Assert.assertNotEquals(cj11, cj31);
+        Assert.assertNotEquals(cd11, cd2);
+        Assert.assertNotEquals(cd11, cd31);
+        
+        Assert.assertNotEquals(cj11, cd11);
+        Assert.assertNotEquals(cj2, cd2);
+        Assert.assertNotEquals(cj31, cd31);
+        Assert.assertNotEquals(cj32, cd32);
+    }
 }
--- a/tests/netx/unit/net/sourceforge/jnlp/util/XDesktopEntryTest.java	Wed Jan 09 17:41:55 2019 +0100
+++ b/tests/netx/unit/net/sourceforge/jnlp/util/XDesktopEntryTest.java	Mon Feb 25 13:41:58 2019 +0100
@@ -44,7 +44,9 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.net.MalformedURLException;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import net.sourceforge.jnlp.InformationDesc;
@@ -226,7 +228,7 @@
 
     @Test
     public void desktopPath() {
-            Assert.assertTrue(XDesktopEntry.getDesktop().getAbsolutePath().startsWith(System.getProperty("user.home")));;
+        Assert.assertTrue(XDesktopEntry.getDesktop().getAbsolutePath().startsWith(System.getProperty("user.home")));;
     }
 
     private static void envToString() {
@@ -251,6 +253,69 @@
         Assert.assertEquals(f2.getName(), f3.getName());
     }
 
+    @Test
+    public void testPosibleFavIConPathparents() throws IOException {
+        List<String> commonResult = new ArrayList<>();
+        commonResult.add("/best/path/file");
+        commonResult.add("/best/path");
+        commonResult.add("/best");
+        commonResult.add("");
+        String path = "/best/path/file";
+        List<String> r = XDesktopEntry.possibleFavIconLocations(path);
+        Assert.assertEquals(r,commonResult);
+        path = "best/path/file";
+        r = XDesktopEntry.possibleFavIconLocations(path);
+        Assert.assertEquals(r,commonResult);
+        path = "best/path/file/";
+        r = XDesktopEntry.possibleFavIconLocations(path);
+        Assert.assertEquals(r,commonResult);
+        path = "/best/path/file/";
+        r = XDesktopEntry.possibleFavIconLocations(path);
+        Assert.assertEquals(r,commonResult);
+        commonResult = new ArrayList<>();
+        commonResult.add("/best\\path\\file");
+        commonResult.add("/best\\path");
+        commonResult.add("/best");
+        commonResult.add("");
+        path = "best\\path\\file";
+        r = XDesktopEntry.possibleFavIconLocations(path);
+        Assert.assertEquals(r,commonResult);
+        path = "best\\path\\file\\";
+        r = XDesktopEntry.possibleFavIconLocations(path);
+        Assert.assertEquals(r,commonResult);
+        path = "/best\\path\\file\\";
+        r = XDesktopEntry.possibleFavIconLocations(path);
+        Assert.assertEquals(r,commonResult);
+        commonResult = new ArrayList<>();
+        commonResult.add("/");
+        commonResult.add("");
+        path = "";
+        r = XDesktopEntry.possibleFavIconLocations(path);
+        Assert.assertEquals(r,commonResult);
+        commonResult = new ArrayList<>();
+        commonResult.add("/ ");
+        commonResult.add("");
+        path = " ";
+        r = XDesktopEntry.possibleFavIconLocations(path);
+        Assert.assertEquals(r,commonResult);
+        commonResult = new ArrayList<>();
+        commonResult.add("/");
+        commonResult.add("");
+        path = "/";
+        r = XDesktopEntry.possibleFavIconLocations(path);
+        Assert.assertEquals(r,commonResult);
+        commonResult = new ArrayList<>();
+        commonResult.add("/not/best\\path/file\\path");
+        commonResult.add("/not/best\\path/file");
+        commonResult.add("/not/best\\path");
+        commonResult.add("/not/best");
+        commonResult.add("/not");
+        commonResult.add("");
+        path = "not/best\\path/file\\path";
+        r = XDesktopEntry.possibleFavIconLocations(path);
+        Assert.assertEquals(r,commonResult);
+    }
+
     private void testHtmlOccurences(boolean html, boolean javaws, boolean menu, AccessWarningPaneComplexReturn.ShortcutResult.Shortcut type, int occurences) throws Exception {
         JNLPRuntime.setHtml(html);
         setIsWebstart(javaws);