You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by ma...@apache.org on 2017/11/12 12:30:16 UTC

[incubator-netbeans] branch master updated: [NETBEANS-96] New PAC Script evaluation environment

This is an automated email from the ASF dual-hosted git repository.

matthiasblaesing pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new e899a7c  [NETBEANS-96] New PAC Script evaluation environment
     new 2dc9605  [NETBEANS-96] New PAC Script evaluation environment
e899a7c is described below

commit e899a7cc7bc2fd450b1b189fb84e249516e03c96
Author: Lars Bruun-Hansen <lb...@gmail.com>
AuthorDate: Sat Oct 21 09:32:05 2017 +0200

    [NETBEANS-96] New PAC Script evaluation environment
    
    Implements a completely new PAC script evaluation
    environment with lots of bug fixes, pluggable and
    with more transparency.
---
 core.network/apichanges.xml                        |   77 ++
 core.network/arch.xml                              | 1251 ++++++++++++++++++++
 core.network/external/binaries-list                |   17 -
 .../nsProxyAutoConfig-mozilla2.0-license.txt       |  478 --------
 core.network/manifest.mf                           |    2 +-
 core.network/nbproject/project.properties          |    6 +-
 core.network/nbproject/project.xml                 |   27 +-
 .../netbeans/core/network/proxy/Bundle.properties  |    4 +-
 .../core/network/proxy/ProxyAutoConfig.java        |  247 +---
 .../core/network/proxy/pac/PacHelperMethods.java   |   48 +
 .../proxy/pac/PacHelperMethodsMicrosoft.java       |  212 ++++
 .../proxy/pac/PacHelperMethodsNetscape.java        |  374 ++++++
 .../core/network/proxy/pac/PacJsEntryFunction.java |   49 +
 .../network/proxy/pac/PacParsingException.java     |   36 +
 .../core/network/proxy/pac/PacScriptEvaluator.java |   83 ++
 .../proxy/pac/PacScriptEvaluatorFactory.java       |   53 +
 .../proxy/pac/PacScriptEvaluatorNoProxy.java       |   57 +
 .../netbeans/core/network/proxy/pac/PacUtils.java  |  356 ++++++
 .../network/proxy/pac/PacValidationException.java  |   46 +
 .../core/network/proxy/pac/datetime/DateRange.java |  174 +++
 .../proxy/pac/datetime/PacUtilsDateTime.java       |  374 ++++++
 .../core/network/proxy/pac/datetime/TimeRange.java |  185 +++
 .../network/proxy/pac/datetime/package-info.java   |   22 +
 .../proxy/pac/impl/ClassFilterPacHelpers.java      |   42 +
 .../proxy/pac/impl/HelperScriptFactory.java        |  139 +++
 .../network/proxy/pac/impl/NbPacHelperMethods.java |  343 ++++++
 .../proxy/pac/impl/NbPacScriptEvaluator.java       |  531 +++++++++
 .../pac/impl/NbPacScriptEvaluatorFactory.java      |   52 +
 .../core/network/proxy/pac/impl/package-info.java  |   26 +
 .../core/network/proxy/pac/package-info.java       |   31 +
 .../netbeans/core/network/utils/HostnameUtils.java |   72 ++
 .../core/network/utils/IpAddressUtils.java         |  526 ++++++++
 .../core/network/utils/IpAddressUtilsFilter.java   |  107 ++
 .../core/network/utils/LocalAddressUtils.java      |  536 +++++++++
 .../core/network/utils/NativeException.java        |   43 +
 .../core/network/utils/SimpleObjCache.java         |  153 +++
 .../utils/hname/linux/HostnameUtilsLinux.java      |   44 +
 .../network/utils/hname/linux/package-info.java    |   23 +
 .../network/utils/hname/mac/HostnameUtilsMac.java  |   48 +
 .../core/network/utils/hname/mac/package-info.java |   23 +
 .../utils/hname/solaris/HostnameUtilsSolaris.java  |   43 +
 .../network/utils/hname/solaris/package-info.java  |   23 +
 .../core/network/utils/hname/unix/CLib.java        |   32 +
 .../utils/hname/unix/HostnameUtilsUnix.java        |   54 +
 .../network/utils/hname/unix/package-info.java     |   23 +
 .../network/utils/hname/win/HostnameUtilsWin.java  |  110 ++
 .../core/network/utils/hname/win/Winsock2Lib.java  |   32 +
 .../core/network/utils/hname/win/package-info.java |   23 +
 .../netbeans/core/network/utils/package-info.java  |   24 +
 .../test/unit/data/pacFiles/wpad space.dat         |   14 +
 core.network/test/unit/data/pacFiles/wpad.dat      |   14 +
 .../data/pacFiles2/pac-test-sandbox-breakout.js    |   32 +
 core.network/test/unit/data/pacFiles2/pac-test1.js |   33 +
 core.network/test/unit/data/pacFiles2/pac-test2.js |   33 +
 core.network/test/unit/data/pacFiles2/pac-test3.js |   39 +
 core.network/test/unit/data/pacFiles2/pac-test4.js |  242 ++++
 .../core/network/proxy/ProxyAutoConfigTest.java    |   12 +-
 .../core/network/proxy/pac/PacEngineTest.java      |  117 ++
 .../network/proxy/pac/PacUtilsDateTimeTest.java    |  160 +++
 .../core/network/proxy/pac/PacUtilsTest.java       |  150 +++
 .../proxy/pac/impl/PacHelperMethodsImplTest.java   |  333 ++++++
 .../org/netbeans/core/network/utils/FakeDns.java   |  216 ++++
 .../core/network/utils/IpAddressUtilsTest.java     |  167 +++
 .../core/network/utils/LocalAddressUtilsTest.java  |   82 ++
 .../core/network/utils/SimpleCacheTest.java        |  129 ++
 libs.jna.platform/manifest.mf                      |    2 +-
 libs.jna.platform/nbproject/project.xml            |    1 +
 nbbuild/build.properties                           |    1 +
 nbbuild/javadoctools/links.xml                     |    1 +
 nbbuild/javadoctools/properties.xml                |    1 +
 nbbuild/javadoctools/replaces.xml                  |    1 +
 71 files changed, 8363 insertions(+), 698 deletions(-)

diff --git a/core.network/apichanges.xml b/core.network/apichanges.xml
new file mode 100644
index 0000000..c2e52a0
--- /dev/null
+++ b/core.network/apichanges.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<?xml-stylesheet type="text/xml" href="../nbbuild/javadoctools/apichanges.xsl"?>
+<!DOCTYPE apichanges PUBLIC "-//NetBeans//DTD API changes list 1.0//EN" "../nbbuild/javadoctools/apichanges.dtd">
+
+<apichanges>
+
+  <apidefs>
+    <apidef name="friend">PAC Script Evaluation Environment API</apidef>
+  </apidefs>
+
+  <changes>
+      <change id="pac-script-evaluator">
+          <api name="friend"/>
+          <summary>PAC Script evaluation environment is now pluggable</summary>
+          <version major="1" minor="12"/>
+          <date day="18" month="10" year="2017"/>
+          <author login="lbruun"/>
+          <compatibility addition="yes" binary="compatible" semantic="compatible" />
+          <description>
+              <p>
+                  The PAC Script Evaluation environment (as part of <code>ProxySelector</code>)
+                  has been completely rewritten and is now pluggable. (albeit for now only for friends)
+              </p>
+          </description>
+          <issue number="NETBEANS-4"/>
+      </change>
+
+  </changes>
+
+  <!-- Now the surrounding HTML text and document structure: -->
+
+  <htmlcontents>
+    <head>
+      <title>Change History for the Friend Network Core APIs</title>
+      <link rel="stylesheet" href="prose.css" type="text/css"/>
+    </head>
+    <body>
+
+<p class="overviewlink"><a href="overview-summary.html">Overview</a></p>
+
+<h1>Introduction</h1>
+
+<p>
+This document lists changes made to the Network Core APIs. 
+    
+</p>
+
+<!-- The actual lists of changes, as summaries and details: -->
+
+      <hr/><standard-changelists module-code-name="org.netbeans.core"/>
+
+      <hr/><p>@FOOTER@</p>
+
+    </body>
+  </htmlcontents>
+
+</apichanges>
diff --git a/core.network/arch.xml b/core.network/arch.xml
new file mode 100644
index 0000000..a1d257f
--- /dev/null
+++ b/core.network/arch.xml
@@ -0,0 +1,1251 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<!DOCTYPE api-answers PUBLIC "-//NetBeans//DTD Arch Answers//EN" "../nbbuild/antsrc/org/netbeans/nbbuild/Arch.dtd" [
+  <!ENTITY api-questions SYSTEM "../nbbuild/antsrc/org/netbeans/nbbuild/Arch-api-questions.xml">
+]>
+
+<api-answers
+  question-version="1.29"
+  author="lbruun"
+>
+
+  &api-questions;
+
+
+<!--
+        <question id="arch-overall" when="init">
+            Describe the overall architecture. 
+            <hint>
+            What will be API for 
+            <a href="http://wiki.netbeans.org/API_Design#Separate_API_for_clients_from_support_API">
+                clients and what support API</a>? 
+            What parts will be pluggable?
+            How will plug-ins be registered? Please use <code>&lt;api type="export"/&gt;</code>
+            to describe your general APIs and specify their
+            <a href="http://wiki.netbeans.org/API_Stability#Private">
+            stability categories</a>.
+            If possible please provide simple diagrams.
+            </hint>
+        </question>
+-->
+ <answer id="arch-overall">
+  <p>
+      The Core Network module provide ProxySelector as well as utilities related
+      to network connections.
+  </p>
+  <p>
+      <b>ProxySelector</b>
+  </p>
+  <p>
+      The role of a ProxySelector is to tell to the rest of Java which 
+      proxy to use for a given URL. There's only one ProxySelector in a given
+      JVM. Most notably the ProxySelector is used by the <code>URLConnection</code> 
+      classes and even the new HTTP client in JDK9 is using it, although it allows
+      more control over this than does the <code>URLConnection</code> classes.
+      Also, the Apache HttpClient is also using the default ProxySelector if you have instructed
+      it to use what is known in Apache HttpClient as <code>SystemDefaultRoutePlanner</code>.
+  </p>
+  <p>
+      This module provides an implementation of a ProxySelector which finds out
+      which proxy to use by looking at preferences the user has stored. 
+      This is trivial for the case where the user has provided explicit proxy
+      information. It is more complex for the case where the user has said 
+      "just use my system's proxy configuration" which is by the way the default
+      in NetBeans. This requires us to go the underlying OS and try to figure
+      out what the user's settings are. In many case case the user will not have
+      explicit proxy setting in the OS either but will simply have "Automatically
+      detect settings" or whatever. This makes it even more complex (see WPAD section
+      below).
+  </p>
+  <p>
+      The ProxySelector is instantiated by the <i>Startup</i> module, but only 
+      if module <i>Core</i> also exists. Thus, for a minimal Platform application
+      which only use the modules from the "Runtime Container", there will be
+      no ProxySelector installed because module <i>Core</i> is not part of the
+      Runtime Container. In this case, the standard ProxySelector in 
+      Java will then be used. This makes sense because a headless application
+      will not have stored Preferences about proxy, etc, and will be much
+      better off by just using the standard ProxySelector (which can be controlled
+      by <a href="https://docs.oracle.com/javase/8/docs/api/java/net/doc-files/net-properties.html">properties</a>).
+  </p>
+  <p>
+      Module <i>Core</i> uses the Global Lookup to search for a ProxySelector,
+      so you can register your own ProxySelector if you don't like the one
+      provided by the Platform (i.e. this module). This will be somewhat difficult
+      though, because in order to implement your own ProxySelector, you would 
+      need read access to the stored ProxySettings. This class is currently
+      only exposed to friends.
+  </p>
+  <p>
+      <b>Proxy Auto-Config (PAC)</b>
+  </p>
+  <p>
+      Very often the proxy configuration on a corporate network is not 
+      given explictly. Instead it is given in a little JavaScript file
+      which clients are supposed to download and execute in order to find 
+      out which proxy to use. This is known as <a href="https://en.wikipedia.org/wiki/Proxy_auto-config">Proxy Auto-Config</a>
+      (or PAC). This module provides a pluggable PAC evaluation environment
+      based on Nashorn. However, it will gracefully degrade to any other JavaScript
+      which may be installed in the JVM. Execution of the downloaded
+      JavaScript code is sandboxed. (only true for Nashorn)
+  </p>
+  <p>
+      If you don't like the PAC evaluation environment provided
+      by this module then you can plug in your own by registering
+      your own <code>PacScriptEvaluatorFactory</code> class in the Global Lookup.
+      If you want to build your own PAC evaluation environment then you can
+      still make use of all the utility classes provided by this module.
+  </p>
+  <p>
+      <b>Locating the PAC script (WPAD)</b>
+  </p>
+  <p>
+      Normally, a Web Browser (in comparison) can locate the PAC script either explicitly, 
+      meaning the URL is directly available from the settings in the OS, or 
+      it can be located via <a href="https://en.wikipedia.org/wiki/Web_Proxy_Auto-Discovery_Protocol">WPAD</a>
+      This module currently only supports the case where URL is directly available
+      from the settings. There's no support for WPAD.
+  </p>
+  </answer>
+
+
+
+<!--
+        <question id="arch-quality" when="init">
+            How will the <a href="http://www.netbeans.org/community/guidelines/q-evangelism.html">quality</a>
+            of your code be tested and 
+            how are future regressions going to be prevented?
+            <hint>
+            What kind of testing do
+            you want to use? How much functionality, in which areas,
+            should be covered by the tests? How you find out that your
+            project was successful?
+            </hint>
+        </question>
+-->
+ <answer id="arch-quality">
+  <p>
+   XXX no answer for arch-quality
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="arch-time" when="init">
+            What are the time estimates of the work?
+            <hint>
+            Please express your estimates of how long the design, implementation,
+            stabilization are likely to last. How many people will be needed to
+            implement this and what is the expected milestone by which the work should be 
+            ready?
+            </hint>
+        </question>
+-->
+ <answer id="arch-time">
+  <p>
+   XXX no answer for arch-time
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="arch-usecases" when="init">
+            <hint>
+                Content of this answer will be displayed as part of page at
+                http://www.netbeans.org/download/dev/javadoc/usecases.html 
+                You can use tags &lt;usecase name="name&gt; regular html description &lt;/usecase&gt;
+                and if you want to use an URL you can prefix if with @TOP@ to begin
+                at the root of your javadoc
+            </hint>
+        
+            Describe the main <a href="http://wiki.netbeans.org/API_Design#The_Importance_of_Being_Use_Case_Oriented">
+            use cases</a> of the new API. Who will use it under
+            what circumstances? What kind of code would typically need to be written
+            to use the module?
+        </question>
+-->
+ <answer id="arch-usecases">
+  <p>
+      <usecase name="Custom ProxySelector" id="custom-proxyselector">
+          You can provide your own ProxySelector instead of the one
+          provided by this module. You do this by registering your 
+          own {@link java.net.ProxySelector}
+          in the Global Lookup. However, make sure you understand that
+          the instantiation of the ProxySelector (i.e. executing its constructor)
+          is part of the main startup path of a NetBeans application. For this
+          reason the constructor better be fast. Defer as much work as possible to 
+          later.
+            <api name="java.net.ProxySelector" category="standard" group="java" type="import" url="@JDK@/java/net/ProxySelector.html" />
+      </usecase>
+      <usecase name="Custom PAC Evaluator" id="custom-pac-evaluator">
+          You can plug in your own PAC evaluator if you are unhappy with
+          the {@link org.netbeans.core.network.proxy.pac.impl.NbPacScriptEvaluator  default one} 
+          provided in this module. You do this by registering your 
+          own {@link org.netbeans.core.network.proxy.pac.PacScriptEvaluatorFactory}
+          in the Global Lookup. This factory must then in turn return an 
+          instance of your custom {@link org.netbeans.core.network.proxy.pac.PacScriptEvaluator}.
+            <api name="org.netbeans.core.network.proxy.pac.PacScriptEvaluatorFactory" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/proxy/pac/PacScriptEvaluatorFactory.html" />
+            <api name="org.netbeans.core.network.proxy.pac.PacScriptEvaluator" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/proxy/pac/PacScriptEvaluator.html" />
+      </usecase>
+      <usecase name="Customizing the default PAC Evaluator" id="custom-pac-evaluator-helpers">
+          Instead of replacing all of the {@link org.netbeans.core.network.proxy.pac.impl.NbPacScriptEvaluator  default PAC evaluator}
+          you can replace only its implementation of the <i>PAC Helper Functions</i>.
+          However, you have to play by rules set by the {@link org.netbeans.core.network.proxy.pac.impl.NbPacScriptEvaluator  default PacScriptEvaluator},
+          namely that all the Helper Functions are implemented in Java (as opposed to JavaScript).
+          Simply register your sub-class of {@link org.netbeans.core.network.proxy.pac.PacHelperMethods}
+          in the Global Lookup and your implementation will automatically be picked
+          up. When creating your own <code>PacHelperMethods</code> you may
+          take benefit from the PAC utility functions provided by {@link org.netbeans.core.network.proxy.pac.PacUtils}
+          and {@link org.netbeans.core.network.proxy.pac.datetime.PacUtilsDateTime},
+          as well as the more general ones provided by {@link org.netbeans.core.network.utils network utils package}.
+            <api name="org.netbeans.core.network.proxy.pac.PacHelperMethods" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/proxy/pac/PacHelperMethods.html" />
+            <api name="org.netbeans.core.network.utils.HostnameUtils" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/utils/HostnameUtils.html" />
+            <api name="org.netbeans.core.network.utils.IpAddressUtils" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/utils/IpAddressUtils.html" />
+            <api name="org.netbeans.core.network.utils.LocalAddressUtils" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/utils/LocalAddressUtils.html" />
+            <api name="org.netbeans.core.network.utils.SimpleObjCache" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/utils/SimpleObjCache.html" />
+            <api name="org.netbeans.core.network.proxy.pac.PacUtils" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/proxy/pac/PacUtils.html" />
+            <api name="org.netbeans.core.network.proxy.pac.datetime.PacUtilsDateTime" category="friend" group="java" type="export" url="@TOP@/org/netbeans/core/network/proxy/pac/datetime/PacUtilsDateTime.html" />
+      </usecase>
+  </p>
+
+ </answer>
+
+
+
+<!--
+        <question id="arch-what" when="init">
+            What is this project good for?
+            <hint>
+            Please provide here a few lines describing the project, 
+            what problem it should solve, provide links to documentation, 
+            specifications, etc.
+            </hint>
+        </question>
+-->
+ <answer id="arch-what">
+  <p>
+      The Core Network module provide ProxySelector as well as utilities related
+      to network connections.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="arch-where" when="impl">
+            Where one can find sources for your module?
+            <hint>
+                Please provide link to the Hg web client at
+                http://hg.netbeans.org/
+                or just use tag defaultanswer generate='here'
+            </hint>
+        </question>
+-->
+ <answer id="arch-where">
+  <defaultanswer generate='here' />
+ </answer>
+
+
+
+<!--
+        <question id="compat-deprecation" when="init">
+            How the introduction of your project influences functionality
+            provided by previous version of the product?
+            <hint>
+            If you are planning to deprecate/remove/change any existing APIs,
+            list them here accompanied with the reason explaining why you
+            are doing so.
+            </hint>
+        </question>
+-->
+ <answer id="compat-deprecation">
+  <p>
+   XXX no answer for compat-deprecation
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="compat-i18n" when="impl">
+            Is your module correctly internationalized?
+            <hint>
+            Correct internationalization means that it obeys instructions 
+            at <a href="http://www.netbeans.org/download/dev/javadoc/org-openide-modules/org/openide/modules/doc-files/i18n-branding.html">
+            NetBeans I18N pages</a>.
+            </hint>
+        </question>
+-->
+ <answer id="compat-i18n">
+  <p>
+   XXX no answer for compat-i18n
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="compat-standards" when="init">
+            Does the module implement or define any standards? Is the 
+            implementation exact or does it deviate somehow?
+        </question>
+-->
+ <answer id="compat-standards">
+  <p>
+      The module implements the <a href="https://en.wikipedia.org/wiki/Proxy_auto-config">Proxy-Auto Config (PAC)</a>
+      specification as first specified by Netscape and then later amended by Microsoft. 
+      The PAC script is a small piece of JavaScript, typically downloaded from the network.
+      The purpose of the PAC script is to determine which proxy to use for a 
+      given URL.
+  </p>
+  <p>      
+      In particular the specification from Netscape was very vaque. This has led to various 
+      inconsistencies between how browsers (or other software) has implemented
+      this. The implementation in this module aims to support just about
+      every corner case and to be as compatible as possible with how the PAC 
+      script is interpreted by various browsers. Whenever the standard is 
+      ambigious, the implementation provided by the module deliberately
+      interprets the specification in the widest possible sense. More information in the Javadoc for
+      {@link org.netbeans.core.network.proxy.pac.impl.NbPacScriptEvaluator}.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="compat-version" when="impl">
+            Can your module coexist with earlier and future
+            versions of itself? Can you correctly read all old settings? Will future
+            versions be able to read your current settings? Can you read
+            or politely ignore settings stored by a future version?
+            
+            <hint>
+            Very helpful for reading settings is to store version number
+            there, so future versions can decide whether how to read/convert
+            the settings and older versions can ignore the new ones.
+            </hint>
+        </question>
+-->
+ <answer id="compat-version">
+  <p>
+   XXX no answer for compat-version
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="dep-jre" when="final">
+            Which version of JRE do you need (1.2, 1.3, 1.4, etc.)?
+            <hint>
+            It is expected that if your module runs on 1.x that it will run 
+            on 1.x+1 if no, state that please. Also describe here cases where
+            you run different code on different versions of JRE and why.
+            </hint>
+        </question>
+-->
+ <answer id="dep-jre">
+  <p>
+      Java 1.8 or later is required.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="dep-jrejdk" when="final">
+            Do you require the JDK or is the JRE enough?
+        </question>
+-->
+ <answer id="dep-jrejdk">
+  <p>
+   Only JRE is needed.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="dep-nb" when="init">
+            What other NetBeans projects and modules does this one depend on?
+            <hint>
+            Depending on other NetBeans projects influnces the ability of
+            users of your work to customize their own branded version of
+            NetBeans by enabling and disabling some modules. Too
+            much dependencies restrict this kind of customization. If that
+            is your case, then you may want to split your functionality into
+            pieces of autoload, eager and regular modules which can be
+            enabled independently. Usually the answer to this question
+            is generated from your <code>project.xml</code> file, but
+            if it is not guessed correctly, you can suppress it by
+            specifying &lt;defaultanswer generate="none"/&gt; and
+            write here your own. Please describe such projects as imported APIs using
+            the <code>&lt;api name="identification" type="import or export" category="stable" url="where is the description" /&gt;</code>.
+            By doing this information gets listed in the summary page of your
+            javadoc.
+            </hint>
+        </question>
+-->
+ <answer id="dep-nb">
+  <defaultanswer generate='here' />
+ </answer>
+
+
+
+<!--
+        <question id="dep-non-nb" when="init">
+            What other projects outside NetBeans does this one depend on?
+            
+            <hint>
+            Depending on 3rd party libraries is always problematic,
+            especially if they are not open source, as that complicates
+            the licensing scheme of NetBeans. Please enumerate your
+            external dependencies here, so it is correctly understood since
+            the begining what are the legal implications of your project.
+            Also please note that
+            some non-NetBeans projects are packaged as NetBeans modules
+            (see <a href="http://libs.netbeans.org/">libraries</a>) and
+            it is preferred to use this approach when more modules may
+            depend and share such third-party libraries.
+            </hint>
+        </question>
+-->
+ <answer id="dep-non-nb">
+  <p>
+   XXX no answer for dep-non-nb
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="dep-platform" when="init">
+            On which platforms does your module run? Does it run in the same
+            way on each?
+            <hint>
+            If you plan any dependency on OS or any usage of native code,
+            please describe why you are doing so and describe how you envision
+            to enforce the portability of your code.
+            Please note that there is a support for <a href="http://www.netbeans.org/download/dev/javadoc/org-openide-modules/org/openide/modules/doc-files/api.html#how-os-specific">OS conditionally
+            enabled modules</a> which together with autoload/eager modules
+            can allow you to enable to provide the best OS aware support
+            on certain OSes while providing compatibility bridge on the not
+            supported ones.
+            Also please list the supported
+            OSes/HW platforms and mentioned the lovest version of JDK required
+            for your project to run on. Also state whether JRE is enough or
+            you really need JDK.
+            </hint>
+        </question>
+-->
+ <answer id="dep-platform">
+  <p>
+      The module uses JNA to look up various information about the host.
+      Currently there's support for Windows dektops, Mac OS X desktops,
+      KDE desktops and Gnome desktops.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="deploy-dependencies" when="final">
+            What do other modules need to do to declare a dependency on this one,
+            in addition to or instead of the normal module dependency declaration
+            (e.g. tokens to require)?
+            <hint>
+                Provide a sample of the actual lines you would add to a module manifest
+                to declare a dependency, for example OpenIDE-Module-Requires: some.token.
+                If other modules should not depend on this module, or should just use a
+                simple regular module dependency, you can just answer "nothing". If you
+                intentionally expose a semistable API to clients using implementation
+                dependencies, you should mention that here (but there is no need to give
+                an example of usage).
+            </hint>
+        </question>
+-->
+ <answer id="deploy-dependencies">
+  <p>
+   XXX no answer for deploy-dependencies
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="deploy-jar" when="impl">
+            Do you deploy just module JAR file(s) or other files as well?
+            <hint>
+            Usually a module consist of one JAR file (perhaps with Class-Path
+            extensions) and also a configuration file that enables it. If you
+            have any other files, use
+            &lt;api group="java.io.File" name="yourname" type="export" category="friend"&gt;...&lt;/api&gt;
+            to define the location, name and stability of your files (of course
+            changing "yourname" and "friend" to suit your needs).
+            
+            If it uses more than one JAR, describe where they are located, how
+            they refer to each other. 
+            If it consist of module JAR(s) and other files, please describe
+            what is their purpose, why other files are necessary. Please 
+            make sure that installation/uninstallation leaves the system 
+            in state as it was before installation.
+            </hint>
+        </question>
+-->
+ <answer id="deploy-jar">
+  <p>
+   XXX no answer for deploy-jar
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="deploy-nbm" when="impl">
+            Can you deploy an NBM via the Update Center?
+            <hint>
+            If not why?
+            </hint>
+        </question>
+-->
+ <answer id="deploy-nbm">
+  <p>
+   Yes
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="deploy-packages" when="init">
+            Are packages of your module made inaccessible by not declaring them
+            public?
+            
+            <hint>
+            By default NetBeans build harness treats all packages are private.
+            If you export some of them - either as public or friend packages,
+            you should have a reason. If the reason is described elsewhere
+            in this document, you can ignore this question.
+            </hint>
+        </question>
+-->
+ <answer id="deploy-packages">
+  <p>
+   XXX no answer for deploy-packages
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="deploy-shared" when="final">
+            Do you need to be installed in the shared location only, or in the user directory only,
+            or can your module be installed anywhere?
+            <hint>
+            Installation location shall not matter, if it does explain why.
+            Consider also whether <code>InstalledFileLocator</code> can help.
+            </hint>
+        </question>
+-->
+ <answer id="deploy-shared">
+  <p>
+   Can be installed anywhere
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="exec-ant-tasks" when="impl">
+            Do you define or register any ant tasks that other can use?
+            
+            <hint>
+            If you provide an ant task that users can use, you need to be very
+            careful about its syntax and behaviour, as it most likely forms an
+	          API for end users and as there is a lot of end users, their reaction
+            when such API gets broken can be pretty strong.
+            </hint>
+        </question>
+-->
+ <answer id="exec-ant-tasks">
+  <p>
+   No
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="exec-classloader" when="impl">
+            Does your code create its own class loader(s)?
+            <hint>
+            A bit unusual. Please explain why and what for.
+            </hint>
+        </question>
+-->
+ <answer id="exec-classloader">
+  <p>
+   No
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="exec-component" when="impl">
+            Is execution of your code influenced by any (string) property
+            of any of your components?
+            
+            <hint>
+            Often <code>JComponent.getClientProperty</code>, <code>Action.getValue</code>
+            or <code>PropertyDescriptor.getValue</code>, etc. are used to influence
+            a behavior of some code. This of course forms an interface that should
+            be documented. Also if one depends on some interface that an object
+            implements (<code>component instanceof Runnable</code>) that forms an
+            API as well.
+            </hint>
+        </question>
+-->
+ <answer id="exec-component">
+  <p>
+   No
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="exec-introspection" when="impl">
+            Does your module use any kind of runtime type information (<code>instanceof</code>,
+            work with <code>java.lang.Class</code>, etc.)?
+            <hint>
+            Check for cases when you have an object of type A and you also
+            expect it to (possibly) be of type B and do some special action. That
+            should be documented. The same applies on operations in meta-level
+            (Class.isInstance(...), Class.isAssignableFrom(...), etc.).
+            </hint>
+        </question>
+-->
+ <answer id="exec-introspection">
+  <p>
+   Yes: Distinguishing between IPv4 and IPv6 instances is by
+      doing (example) : <code>inetAddress instanceof Inet6Address</code>.
+      This seems to be the generally accepted way of doing it.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="exec-privateaccess" when="final">
+            Are you aware of any other parts of the system calling some of 
+            your methods by reflection?
+            <hint>
+            If so, describe the "contract" as an API. Likely private or friend one, but
+            still API and consider rewrite of it.
+            </hint>
+        </question>
+-->
+ <answer id="exec-privateaccess">
+  <p>
+      No.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="exec-process" when="impl">
+            Do you execute an external process from your module? How do you ensure
+            that the result is the same on different platforms? Do you parse output?
+            Do you depend on result code?
+            <hint>
+            If you feed an input, parse the output please declare that as an API.
+            </hint>
+        </question>
+-->
+ <answer id="exec-process">
+  <p>
+   XXX no answer for exec-process
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="exec-property" when="impl">
+            Is execution of your code influenced by any environment or
+            Java system (<code>System.getProperty</code>) property?
+            On a similar note, is there something interesting that you
+            pass to <code>java.util.logging.Logger</code>? Or do you observe
+            what others log?
+            <hint>
+            If there is a property that can change the behavior of your 
+            code, somebody will likely use it. You should describe what it does 
+            and the <a href="http://wiki.netbeans.org/API_Stability">stability category</a>
+            of this API. You may use
+            <pre>
+                &lt;api type="export" group="property" name="id" category="private" url="http://..."&gt;
+                    description of the property, where it is used, what it influence, etc.
+                &lt;/api&gt;            
+            </pre>
+            </hint>
+        </question>
+-->
+ <answer id="exec-property">
+  <p>
+   No
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="exec-reflection" when="impl">
+            Does your code use Java Reflection to execute other code?
+            <hint>
+            This usually indicates a missing or insufficient API in the other
+            part of the system. If the other side is not aware of your dependency
+            this contract can be easily broken.
+            </hint>
+        </question>
+-->
+ <answer id="exec-reflection">
+  <p>
+   Yes. It detects the presence of Nashorn (as opposed to say Rhino) by way 
+      of reflection. In particular it needs to know if the Java version is
+      Java 8u40 as Nashorn was greatly enhanced in that update and was more
+      or less useless (for our purpose) before this time. The use of reflection
+      means the code will gracefully 'degrade' to whatever script engine is
+      available if we are not on Java 8u40 or later.
+  </p>
+  <p>
+   For testing only a dirty hack is used in our <code>FakeDns</code> class.
+   This installs itself as a preferred name service in Java. This is done
+   via reflection. It also uses proprietary <code>sun.*</code> package.
+   However, it is only used for unit testing.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="exec-threading" when="init">
+            What threading models, if any, does your module adhere to? How the
+            project behaves with respect to threading?
+            <hint>
+                Is your API threadsafe? Can it be accessed from any threads or
+                just from some dedicated ones? Any special relation to AWT and
+                its Event Dispatch thread? Also
+                if your module calls foreign APIs which have a specific threading model,
+                indicate how you comply with the requirements for multithreaded access
+                (synchronization, mutexes, etc.) applicable to those APIs.
+                If your module defines any APIs, or has complex internal structures
+                that might be used from multiple threads, declare how you protect
+                data against concurrent access, race conditions, deadlocks, etc.,
+                and whether such rules are enforced by runtime warnings, errors, assertions, etc.
+                Examples: a class might be non-thread-safe (like Java Collections); might
+                be fully thread-safe (internal locking); might require access through a mutex
+                (and may or may not automatically acquire that mutex on behalf of a client method);
+                might be able to run only in the event queue; etc.
+                Also describe when any events are fired: synchronously, asynchronously, etc.
+                Ideas: <a href="http://core.netbeans.org/proposals/threading/index.html#recommendations">Threading Recommendations</a> (in progress)
+            </hint>
+        </question>
+-->
+ <answer id="exec-threading">
+  <p>
+   Documented in Javadoc where relevant.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="format-clipboard" when="impl">
+            Which data flavors (if any) does your code read from or insert to
+            the clipboard (by access to clipboard on means calling methods on <code>java.awt.datatransfer.Transferable</code>?
+            
+            <hint>
+            Often Node's deal with clipboard by usage of <code>Node.clipboardCopy, Node.clipboardCut and Node.pasteTypes</code>.
+            Check your code for overriding these methods.
+            </hint>
+        </question>
+-->
+ <answer id="format-clipboard">
+  <p>
+      Not applicable.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="format-dnd" when="impl">
+            Which protocols (if any) does your code understand during Drag &amp; Drop?
+            <hint>
+            Often Node's deal with clipboard by usage of <code>Node.drag, Node.getDropType</code>. 
+            Check your code for overriding these methods. Btw. if they are not overridden, they
+            by default delegate to <code>Node.clipboardCopy, Node.clipboardCut and Node.pasteTypes</code>.
+            </hint>
+        </question>
+-->
+ <answer id="format-dnd">
+  <p>
+       Not applicable.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="format-types" when="impl">
+            Which protocols and file formats (if any) does your module read or write on disk,
+            or transmit or receive over the network? Do you generate an ant build script?
+            Can it be edited and modified? 
+            
+            <hint>
+            <p>
+            Files can be read and written by other programs, modules and users. If they influence
+            your behaviour, make sure you either document the format or claim that it is a private
+            api (using the &lt;api&gt; tag). 
+            </p>
+            
+            <p>
+            If you generate an ant build file, this is very likely going to be seen by end users and
+            they will be attempted to edit it. You should be ready for that and provide here a link
+            to documentation that you have for such purposes and also describe how you are going to
+            understand such files during next release, when you (very likely) slightly change the 
+            format.
+            </p>
+            </hint>
+        </question>
+-->
+ <answer id="format-types">
+  <p>
+       Not applicable.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="lookup-lookup" when="init">
+            Does your module use <code>org.openide.util.Lookup</code>
+            or any similar technology to find any components to communicate with? Which ones?
+            
+            <hint>
+            NetBeans is build around a generic registry of services called
+            lookup. It is preferable to use it for registration and discovery
+            if possible. See
+            <a href="http://www.netbeans.org/download/dev/javadoc/org-openide-util/org/openide/util/lookup/doc-files/index.html">
+            The Solution to Comunication Between Components
+            </a>. If you do not plan to use lookup and insist usage
+            of other solution, then please describe why it is not working for
+            you.
+            <br/>
+            When filling the final version of your arch document, please
+            describe the interfaces you are searching for, where 
+            are defined, whether you are searching for just one or more of them,
+            if the order is important, etc. Also classify the stability of such
+            API contract. Use &lt;api group=&amp;lookup&amp; /&gt; tag, so
+            your information gets listed in the summary page of your javadoc.
+            </hint>
+        </question>
+-->
+ <answer id="lookup-lookup">
+  <p>
+   XXX no answer for lookup-lookup
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="lookup-register" when="final">
+            Do you register anything into lookup for other code to find?
+            <hint>
+            Do you register using layer file or using a declarative annotation such as <code>@ServiceProvider</code>?
+            Who is supposed to find your component?
+            </hint>
+        </question>
+-->
+ <answer id="lookup-register">
+  <p>
+      The, this module registers a NetBeans <code>ProxySelector</code> into
+      the Global Lookup. It also registers a <code>PacScriptEvaluatorFactory</code>
+      which is used by the NetBeans ProxySelector itself.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="lookup-remove" when="final">
+            Do you remove entries of other modules from lookup?
+            <hint>
+            Why? Of course, that is possible, but it can be dangerous. Is the module
+            your are masking resource from aware of what you are doing?
+            </hint>
+        </question>
+-->
+ <answer id="lookup-remove">
+  <p>
+   No
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="perf-exit" when="final">
+            Does your module run any code on exit?
+        </question>
+-->
+ <answer id="perf-exit">
+  <p>
+   No
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="perf-huge_dialogs" when="final">
+            Does your module contain any dialogs or wizards with a large number of
+            GUI controls such as combo boxes, lists, trees, or text areas?
+        </question>
+-->
+ <answer id="perf-huge_dialogs">
+  <p>
+   No
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="perf-limit" when="init">
+            Are there any hard-coded or practical limits in the number or size of
+            elements your code can handle?
+            <hint>
+                Most of algorithms have increasing memory and speed complexity
+                with respect to size of data they operate on. What is the critical
+                part of your project that can be seen as a bottleneck with
+                respect to speed or required memory? What are the practical
+                sizes of data you tested your project with? What is your estimate
+                of potential size of data that would cause visible performance
+                problems? Is there some kind of check to detect such situation
+                and prevent "hard" crashes - for example the CloneableEditorSupport
+                checks for size of a file to be opened in editor
+                and if it is larger than 1Mb it shows a dialog giving the
+                user the right to decide - e.g. to cancel or commit suicide.
+            </hint>
+        </question>
+-->
+ <answer id="perf-limit">
+  <p>
+   No
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="perf-mem" when="final">
+            How much memory does your component consume? Estimate
+            with a relation to the number of windows, etc.
+        </question>
+-->
+ <answer id="perf-mem">
+  <p>
+   XXX no answer for perf-mem
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="perf-menus" when="final">
+            Does your module use dynamically updated context menus, or
+            context-sensitive actions with complicated and slow enablement logic?
+            <hint>
+                If you do a lot of tricks when adding actions to regular or context menus, you can significantly
+                slow down display of the menu, even when the user is not using your action. Pay attention to
+                actions you add to the main menu bar, and to context menus of foreign nodes or components. If
+                the action is conditionally enabled, or changes its display dynamically, you need to check the
+                impact on performance. In some cases it may be more appropriate to make a simple action that is
+                always enabled but does more detailed checks in a dialog if it is actually run.
+            </hint>
+        </question>
+-->
+ <answer id="perf-menus">
+  <p>
+   Not applicable. There's no GUI in this module.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="perf-progress" when="final">
+            Does your module execute any long-running tasks?
+            
+            <hint>Long running tasks should never block 
+            AWT thread as it badly hurts the UI
+            <a href="http://performance.netbeans.org/responsiveness/issues.html">
+            responsiveness</a>.
+            Tasks like connecting over
+            network, computing huge amount of data, compilation
+            be done asynchronously (for example
+            using <code>RequestProcessor</code>), definitively it should 
+            not block AWT thread.
+            </hint>
+        </question>
+-->
+ <answer id="perf-progress">
+  <p>
+   Yes. It executes tasks such as downloading PAC script from
+      network. This is done in a RequestProcessor.
+      It also provides utility methods for doing name lookup with
+      a timeout.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="perf-scale" when="init">
+            Which external criteria influence the performance of your
+            program (size of file in editor, number of files in menu, 
+            in source directory, etc.) and how well your code scales?
+            <hint>
+            Please include some estimates, there are other more detailed 
+            questions to answer in later phases of implementation. 
+            </hint>
+        </question>
+-->
+ <answer id="perf-scale">
+  <p>
+   XXX no answer for perf-scale
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="perf-spi" when="init">
+            How the performance of the plugged in code will be enforced?
+            <hint>
+            If you allow foreign code to be plugged into your own module, how
+            do you enforce that it will behave correctly and quickly and will not
+            negatively influence the performance of your own module?
+            </hint>
+        </question>
+-->
+ <answer id="perf-spi">
+  <p>
+   XXX no answer for perf-spi
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="perf-startup" when="final">
+            Does your module run any code on startup?
+        </question>
+-->
+ <answer id="perf-startup">
+  <p>
+   Yes. The initialization of the ProxySelector (i.e. calling its constructor)
+      is done as part of the platform startup process in module Startup. Moreso, 
+      it is done on the platform main thread. In other words: The constructor needs
+      to be quick as it influences platform startup time. At the moment
+      this constructor is fairly lazy. For example it postpones download of 
+      PAC file. However, the constructor can be further improved in terms
+      of startup speed.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="perf-wakeup" when="final">
+            Does any piece of your code wake up periodically and do something
+            even when the system is otherwise idle (no user interaction)?
+        </question>
+-->
+ <answer id="perf-wakeup">
+  <p>
+   No
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="resources-file" when="final">
+            Does your module use <code>java.io.File</code> directly?
+            
+            <hint>
+            NetBeans provide a logical wrapper over plain files called 
+            <code>org.openide.filesystems.FileObject</code> that
+            provides uniform access to such resources and is the preferred
+            way that should be used. But of course there can be situations when
+            this is not suitable.
+            </hint>
+        </question>
+-->
+ <answer id="resources-file">
+  <p>
+   No
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="resources-layer" when="final">
+            Does your module provide own layer? Does it create any files or
+            folders in it? What it is trying to communicate by that and with which 
+            components?
+            
+            <hint>
+            NetBeans allows automatic and declarative installation of resources 
+            by module layers. Module register files into appropriate places
+            and other components use that information to perform their task
+            (build menu, toolbar, window layout, list of templates, set of
+            options, etc.). 
+            </hint>
+        </question>
+-->
+ <answer id="resources-layer">
+  <p>
+   XXX no answer for resources-layer
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="resources-mask" when="final">
+            Does your module mask/hide/override any resources provided by other modules in
+            their layers?
+            
+            <hint>
+            If you mask a file provided by another module, you probably depend
+            on that and do not want the other module to (for example) change
+            the file's name. That module shall thus make that file available as an API
+            of some stability category.
+            </hint>
+        </question>
+-->
+ <answer id="resources-mask">
+  <p>
+   No
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="resources-preferences" when="final">
+            Does your module uses preferences via Preferences API? Does your module use NbPreferences or
+            or regular JDK Preferences ? Does it read, write or both ? 
+            Does it share preferences with other modules ? If so, then why ?
+            <hint>
+                You may use
+                    &lt;api type="export" group="preferences"
+                    name="preference node name" category="private"&gt;
+                    description of individual keys, where it is used, what it
+                    influences, whether the module reads/write it, etc.
+                    &lt;/api&gt;
+                Due to XML ID restrictions, rather than /org/netbeans/modules/foo give the "name" as org.netbeans.modules.foo.
+                Note that if you use NbPreferences this name will then be the same as the code name base of the module.
+            </hint>
+        </question>
+-->
+ <answer id="resources-preferences">
+  <p>
+      The module reads Proxy Preferences using <code>NbPreferences</code>.
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="resources-read" when="final">
+            Does your module read any resources from layers? For what purpose?
+            
+            <hint>
+            As this is some kind of intermodule dependency, it is a kind of API.
+            Please describe it and classify according to 
+            <a href="http://wiki.netbeans.org/API_Design#What_is_an_API.3F">
+            common stability categories</a>.
+            </hint>
+        </question>
+-->
+ <answer id="resources-read">
+  <p>
+   XXX no answer for resources-read
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="security-grant" when="final">
+            Does your code grant additional rights to some other code?
+            <hint>Avoid using a class loader that adds extra
+            permissions to loaded code unless really necessary.
+            Also note that your API implementation
+            can also expose unneeded permissions to enemy code by
+            calling AccessController.doPrivileged().</hint>
+        </question>
+-->
+ <answer id="security-grant">
+  <p>
+   XXX no answer for security-grant
+  </p>
+ </answer>
+
+
+
+<!--
+        <question id="security-policy" when="final">
+            Does your functionality require modifications to the standard policy file?
+            <hint>Your code might pass control to third-party code not
+            coming from trusted domains. This could be code downloaded over the
+            network or code coming from libraries that are not bundled
+            with NetBeans. Which permissions need to be granted to which domains?</hint>
+        </question>
+-->
+ <answer id="security-policy">
+  <p>
+   No
+  </p>
+ </answer>
+
+</api-answers>
diff --git a/core.network/external/binaries-list b/core.network/external/binaries-list
deleted file mode 100644
index 87372f1..0000000
--- a/core.network/external/binaries-list
+++ /dev/null
@@ -1,17 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-22C41D62B7BD70C00603B2CAE75406414224CF9F nsProxyAutoConfig-mozilla2.0.js
diff --git a/core.network/external/nsProxyAutoConfig-mozilla2.0-license.txt b/core.network/external/nsProxyAutoConfig-mozilla2.0-license.txt
deleted file mode 100644
index ac0e2db..0000000
--- a/core.network/external/nsProxyAutoConfig-mozilla2.0-license.txt
+++ /dev/null
@@ -1,478 +0,0 @@
-Name: nsProxyAutoConfig.js
-Version: mozilla2.0
-Description: Implementation of PAC Utils API used by proxy auto configuration (PAC) files. Required for resolution of proxies defined using these APIs.
-License: MPL-1.1
-Origin: Mozilla
-Files: nsProxyAutoConfig-mozilla2.0.js
-URL: http://mxr.mozilla.org/mozilla2.0/source/netwerk/base/src/nsProxyAutoConfig.js
-
-                          MOZILLA PUBLIC LICENSE
-                                Version 1.1
-
-                              ---------------
-
-1. Definitions.
-
-     1.0.1. "Commercial Use" means distribution or otherwise making the
-     Covered Code available to a third party.
-
-     1.1. "Contributor" means each entity that creates or contributes to
-     the creation of Modifications.
-
-     1.2. "Contributor Version" means the combination of the Original
-     Code, prior Modifications used by a Contributor, and the Modifications
-     made by that particular Contributor.
-
-     1.3. "Covered Code" means the Original Code or Modifications or the
-     combination of the Original Code and Modifications, in each case
-     including portions thereof.
-
-     1.4. "Electronic Distribution Mechanism" means a mechanism generally
-     accepted in the software development community for the electronic
-     transfer of data.
-
-     1.5. "Executable" means Covered Code in any form other than Source
-     Code.
-
-     1.6. "Initial Developer" means the individual or entity identified
-     as the Initial Developer in the Source Code notice required by Exhibit
-     A.
-
-     1.7. "Larger Work" means a work which combines Covered Code or
-     portions thereof with code not governed by the terms of this License.
-
-     1.8. "License" means this document.
-
-     1.8.1. "Licensable" means having the right to grant, to the maximum
-     extent possible, whether at the time of the initial grant or
-     subsequently acquired, any and all of the rights conveyed herein.
-
-     1.9. "Modifications" means any addition to or deletion from the
-     substance or structure of either the Original Code or any previous
-     Modifications. When Covered Code is released as a series of files, a
-     Modification is:
-          A. Any addition to or deletion from the contents of a file
-          containing Original Code or previous Modifications.
-
-          B. Any new file that contains any part of the Original Code or
-          previous Modifications.
-
-     1.10. "Original Code" means Source Code of computer software code
-     which is described in the Source Code notice required by Exhibit A as
-     Original Code, and which, at the time of its release under this
-     License is not already Covered Code governed by this License.
-
-     1.10.1. "Patent Claims" means any patent claim(s), now owned or
-     hereafter acquired, including without limitation,  method, process,
-     and apparatus claims, in any patent Licensable by grantor.
-
-     1.11. "Source Code" means the preferred form of the Covered Code for
-     making modifications to it, including all modules it contains, plus
-     any associated interface definition files, scripts used to control
-     compilation and installation of an Executable, or source code
-     differential comparisons against either the Original Code or another
-     well known, available Covered Code of the Contributor's choice. The
-     Source Code can be in a compressed or archival form, provided the
-     appropriate decompression or de-archiving software is widely available
-     for no charge.
-
-     1.12. "You" (or "Your")  means an individual or a legal entity
-     exercising rights under, and complying with all of the terms of, this
-     License or a future version of this License issued under Section 6.1.
-     For legal entities, "You" includes any entity which controls, is
-     controlled by, or is under common control with You. For purposes of
-     this definition, "control" means (a) the power, direct or indirect,
-     to cause the direction or management of such entity, whether by
-     contract or otherwise, or (b) ownership of more than fifty percent
-     (50%) of the outstanding shares or beneficial ownership of such
-     entity.
-
-2. Source Code License.
-
-     2.1. The Initial Developer Grant.
-     The Initial Developer hereby grants You a world-wide, royalty-free,
-     non-exclusive license, subject to third party intellectual property
-     claims:
-          (a)  under intellectual property rights (other than patent or
-          trademark) Licensable by Initial Developer to use, reproduce,
-          modify, display, perform, sublicense and distribute the Original
-          Code (or portions thereof) with or without Modifications, and/or
-          as part of a Larger Work; and
-
-          (b) under Patents Claims infringed by the making, using or
-          selling of Original Code, to make, have made, use, practice,
-          sell, and offer for sale, and/or otherwise dispose of the
-          Original Code (or portions thereof).
-
-          (c) the licenses granted in this Section 2.1(a) and (b) are
-          effective on the date Initial Developer first distributes
-          Original Code under the terms of this License.
-
-          (d) Notwithstanding Section 2.1(b) above, no patent license is
-          granted: 1) for code that You delete from the Original Code; 2)
-          separate from the Original Code;  or 3) for infringements caused
-          by: i) the modification of the Original Code or ii) the
-          combination of the Original Code with other software or devices.
-
-     2.2. Contributor Grant.
-     Subject to third party intellectual property claims, each Contributor
-     hereby grants You a world-wide, royalty-free, non-exclusive license
-
-          (a)  under intellectual property rights (other than patent or
-          trademark) Licensable by Contributor, to use, reproduce, modify,
-          display, perform, sublicense and distribute the Modifications
-          created by such Contributor (or portions thereof) either on an
-          unmodified basis, with other Modifications, as Covered Code
-          and/or as part of a Larger Work; and
-
-          (b) under Patent Claims infringed by the making, using, or
-          selling of  Modifications made by that Contributor either alone
-          and/or in combination with its Contributor Version (or portions
-          of such combination), to make, use, sell, offer for sale, have
-          made, and/or otherwise dispose of: 1) Modifications made by that
-          Contributor (or portions thereof); and 2) the combination of
-          Modifications made by that Contributor with its Contributor
-          Version (or portions of such combination).
-
-          (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
-          effective on the date Contributor first makes Commercial Use of
-          the Covered Code.
-
-          (d)    Notwithstanding Section 2.2(b) above, no patent license is
-          granted: 1) for any code that Contributor has deleted from the
-          Contributor Version; 2)  separate from the Contributor Version;
-          3)  for infringements caused by: i) third party modifications of
-          Contributor Version or ii)  the combination of Modifications made
-          by that Contributor with other software  (except as part of the
-          Contributor Version) or other devices; or 4) under Patent Claims
-          infringed by Covered Code in the absence of Modifications made by
-          that Contributor.
-
-3. Distribution Obligations.
-
-     3.1. Application of License.
-     The Modifications which You create or to which You contribute are
-     governed by the terms of this License, including without limitation
-     Section 2.2. The Source Code version of Covered Code may be
-     distributed only under the terms of this License or a future version
-     of this License released under Section 6.1, and You must include a
-     copy of this License with every copy of the Source Code You
-     distribute. You may not offer or impose any terms on any Source Code
-     version that alters or restricts the applicable version of this
-     License or the recipients' rights hereunder. However, You may include
-     an additional document offering the additional rights described in
-     Section 3.5.
-
-     3.2. Availability of Source Code.
-     Any Modification which You create or to which You contribute must be
-     made available in Source Code form under the terms of this License
-     either on the same media as an Executable version or via an accepted
-     Electronic Distribution Mechanism to anyone to whom you made an
-     Executable version available; and if made available via Electronic
-     Distribution Mechanism, must remain available for at least twelve (12)
-     months after the date it initially became available, or at least six
-     (6) months after a subsequent version of that particular Modification
-     has been made available to such recipients. You are responsible for
-     ensuring that the Source Code version remains available even if the
-     Electronic Distribution Mechanism is maintained by a third party.
-
-     3.3. Description of Modifications.
-     You must cause all Covered Code to which You contribute to contain a
-     file documenting the changes You made to create that Covered Code and
-     the date of any change. You must include a prominent statement that
-     the Modification is derived, directly or indirectly, from Original
-     Code provided by the Initial Developer and including the name of the
-     Initial Developer in (a) the Source Code, and (b) in any notice in an
-     Executable version or related documentation in which You describe the
-     origin or ownership of the Covered Code.
-
-     3.4. Intellectual Property Matters
-          (a) Third Party Claims.
-          If Contributor has knowledge that a license under a third party's
-          intellectual property rights is required to exercise the rights
-          granted by such Contributor under Sections 2.1 or 2.2,
-          Contributor must include a text file with the Source Code
-          distribution titled "LEGAL" which describes the claim and the
-          party making the claim in sufficient detail that a recipient will
-          know whom to contact. If Contributor obtains such knowledge after
-          the Modification is made available as described in Section 3.2,
-          Contributor shall promptly modify the LEGAL file in all copies
-          Contributor makes available thereafter and shall take other steps
-          (such as notifying appropriate mailing lists or newsgroups)
-          reasonably calculated to inform those who received the Covered
-          Code that new knowledge has been obtained.
-
-          (b) Contributor APIs.
-          If Contributor's Modifications include an application programming
-          interface and Contributor has knowledge of patent licenses which
-          are reasonably necessary to implement that API, Contributor must
-          also include this information in the LEGAL file.
-
-               (c)    Representations.
-          Contributor represents that, except as disclosed pursuant to
-          Section 3.4(a) above, Contributor believes that Contributor's
-          Modifications are Contributor's original creation(s) and/or
-          Contributor has sufficient rights to grant the rights conveyed by
-          this License.
-
-     3.5. Required Notices.
-     You must duplicate the notice in Exhibit A in each file of the Source
-     Code.  If it is not possible to put such notice in a particular Source
-     Code file due to its structure, then You must include such notice in a
-     location (such as a relevant directory) where a user would be likely
-     to look for such a notice.  If You created one or more Modification(s)
-     You may add your name as a Contributor to the notice described in
-     Exhibit A.  You must also duplicate this License in any documentation
-     for the Source Code where You describe recipients' rights or ownership
-     rights relating to Covered Code.  You may choose to offer, and to
-     charge a fee for, warranty, support, indemnity or liability
-     obligations to one or more recipients of Covered Code. However, You
-     may do so only on Your own behalf, and not on behalf of the Initial
-     Developer or any Contributor. You must make it absolutely clear than
-     any such warranty, support, indemnity or liability obligation is
-     offered by You alone, and You hereby agree to indemnify the Initial
-     Developer and every Contributor for any liability incurred by the
-     Initial Developer or such Contributor as a result of warranty,
-     support, indemnity or liability terms You offer.
-
-     3.6. Distribution of Executable Versions.
-     You may distribute Covered Code in Executable form only if the
-     requirements of Section 3.1-3.5 have been met for that Covered Code,
-     and if You include a notice stating that the Source Code version of
-     the Covered Code is available under the terms of this License,
-     including a description of how and where You have fulfilled the
-     obligations of Section 3.2. The notice must be conspicuously included
-     in any notice in an Executable version, related documentation or
-     collateral in which You describe recipients' rights relating to the
-     Covered Code. You may distribute the Executable version of Covered
-     Code or ownership rights under a license of Your choice, which may
-     contain terms different from this License, provided that You are in
-     compliance with the terms of this License and that the license for the
-     Executable version does not attempt to limit or alter the recipient's
-     rights in the Source Code version from the rights set forth in this
-     License. If You distribute the Executable version under a different
-     license You must make it absolutely clear that any terms which differ
-     from this License are offered by You alone, not by the Initial
-     Developer or any Contributor. You hereby agree to indemnify the
-     Initial Developer and every Contributor for any liability incurred by
-     the Initial Developer or such Contributor as a result of any such
-     terms You offer.
-
-     3.7. Larger Works.
-     You may create a Larger Work by combining Covered Code with other code
-     not governed by the terms of this License and distribute the Larger
-     Work as a single product. In such a case, You must make sure the
-     requirements of this License are fulfilled for the Covered Code.
-
-4. Inability to Comply Due to Statute or Regulation.
-
-     If it is impossible for You to comply with any of the terms of this
-     License with respect to some or all of the Covered Code due to
-     statute, judicial order, or regulation then You must: (a) comply with
-     the terms of this License to the maximum extent possible; and (b)
-     describe the limitations and the code they affect. Such description
-     must be included in the LEGAL file described in Section 3.4 and must
-     be included with all distributions of the Source Code. Except to the
-     extent prohibited by statute or regulation, such description must be
-     sufficiently detailed for a recipient of ordinary skill to be able to
-     understand it.
-
-5. Application of this License.
-
-     This License applies to code to which the Initial Developer has
-     attached the notice in Exhibit A and to related Covered Code.
-
-6. Versions of the License.
-
-     6.1. New Versions.
-     Netscape Communications Corporation ("Netscape") may publish revised
-     and/or new versions of the License from time to time. Each version
-     will be given a distinguishing version number.
-
-     6.2. Effect of New Versions.
-     Once Covered Code has been published under a particular version of the
-     License, You may always continue to use it under the terms of that
-     version. You may also choose to use such Covered Code under the terms
-     of any subsequent version of the License published by Netscape. No one
-     other than Netscape has the right to modify the terms applicable to
-     Covered Code created under this License.
-
-     6.3. Derivative Works.
-     If You create or use a modified version of this License (which you may
-     only do in order to apply it to code which is not already Covered Code
-     governed by this License), You must (a) rename Your license so that
-     the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
-     "MPL", "NPL" or any confusingly similar phrase do not appear in your
-     license (except to note that your license differs from this License)
-     and (b) otherwise make it clear that Your version of the license
-     contains terms which differ from the Mozilla Public License and
-     Netscape Public License. (Filling in the name of the Initial
-     Developer, Original Code or Contributor in the notice described in
-     Exhibit A shall not of themselves be deemed to be modifications of
-     this License.)
-
-7. DISCLAIMER OF WARRANTY.
-
-     COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
-     WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
-     WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
-     DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
-     THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
-     IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
-     YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
-     COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
-     OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
-     ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-8. TERMINATION.
-
-     8.1.  This License and the rights granted hereunder will terminate
-     automatically if You fail to comply with terms herein and fail to cure
-     such breach within 30 days of becoming aware of the breach. All
-     sublicenses to the Covered Code which are properly granted shall
-     survive any termination of this License. Provisions which, by their
-     nature, must remain in effect beyond the termination of this License
-     shall survive.
-
-     8.2.  If You initiate litigation by asserting a patent infringement
-     claim (excluding declatory judgment actions) against Initial Developer
-     or a Contributor (the Initial Developer or Contributor against whom
-     You file such action is referred to as "Participant")  alleging that:
-
-     (a)  such Participant's Contributor Version directly or indirectly
-     infringes any patent, then any and all rights granted by such
-     Participant to You under Sections 2.1 and/or 2.2 of this License
-     shall, upon 60 days notice from Participant terminate prospectively,
-     unless if within 60 days after receipt of notice You either: (i)
-     agree in writing to pay Participant a mutually agreeable reasonable
-     royalty for Your past and future use of Modifications made by such
-     Participant, or (ii) withdraw Your litigation claim with respect to
-     the Contributor Version against such Participant.  If within 60 days
-     of notice, a reasonable royalty and payment arrangement are not
-     mutually agreed upon in writing by the parties or the litigation claim
-     is not withdrawn, the rights granted by Participant to You under
-     Sections 2.1 and/or 2.2 automatically terminate at the expiration of
-     the 60 day notice period specified above.
-
-     (b)  any software, hardware, or device, other than such Participant's
-     Contributor Version, directly or indirectly infringes any patent, then
-     any rights granted to You by such Participant under Sections 2.1(b)
-     and 2.2(b) are revoked effective as of the date You first made, used,
-     sold, distributed, or had made, Modifications made by that
-     Participant.
-
-     8.3.  If You assert a patent infringement claim against Participant
-     alleging that such Participant's Contributor Version directly or
-     indirectly infringes any patent where such claim is resolved (such as
-     by license or settlement) prior to the initiation of patent
-     infringement litigation, then the reasonable value of the licenses
-     granted by such Participant under Sections 2.1 or 2.2 shall be taken
-     into account in determining the amount or value of any payment or
-     license.
-
-     8.4.  In the event of termination under Sections 8.1 or 8.2 above,
-     all end user license agreements (excluding distributors and resellers)
-     which have been validly granted by You or any distributor hereunder
-     prior to termination shall survive termination.
-
-9. LIMITATION OF LIABILITY.
-
-     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
-     (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
-     DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
-     OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
-     ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
-     CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
-     WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
-     COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
-     INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
-     LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
-     RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
-     PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
-     EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
-     THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-10. U.S. GOVERNMENT END USERS.
-
-     The Covered Code is a "commercial item," as that term is defined in
-     48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
-     software" and "commercial computer software documentation," as such
-     terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
-     C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
-     all U.S. Government End Users acquire Covered Code with only those
-     rights set forth herein.
-
-11. MISCELLANEOUS.
-
-     This License represents the complete agreement concerning subject
-     matter hereof. If any provision of this License is held to be
-     unenforceable, such provision shall be reformed only to the extent
-     necessary to make it enforceable. This License shall be governed by
-     California law provisions (except to the extent applicable law, if
-     any, provides otherwise), excluding its conflict-of-law provisions.
-     With respect to disputes in which at least one party is a citizen of,
-     or an entity chartered or registered to do business in the United
-     States of America, any litigation relating to this License shall be
-     subject to the jurisdiction of the Federal Courts of the Northern
-     District of California, with venue lying in Santa Clara County,
-     California, with the losing party responsible for costs, including
-     without limitation, court costs and reasonable attorneys' fees and
-     expenses. The application of the United Nations Convention on
-     Contracts for the International Sale of Goods is expressly excluded.
-     Any law or regulation which provides that the language of a contract
-     shall be construed against the drafter shall not apply to this
-     License.
-
-12. RESPONSIBILITY FOR CLAIMS.
-
-     As between Initial Developer and the Contributors, each party is
-     responsible for claims and damages arising, directly or indirectly,
-     out of its utilization of rights under this License and You agree to
-     work with Initial Developer and Contributors to distribute such
-     responsibility on an equitable basis. Nothing herein is intended or
-     shall be deemed to constitute any admission of liability.
-
-13. MULTIPLE-LICENSED CODE.
-
-     Initial Developer may designate portions of the Covered Code as
-     "Multiple-Licensed".  "Multiple-Licensed" means that the Initial
-     Developer permits you to utilize portions of the Covered Code under
-     Your choice of the NPL or the alternative licenses, if any, specified
-     by the Initial Developer in the file described in Exhibit A.
-
-EXHIBIT A -Mozilla Public License.
-
-     ``The contents of this file are subject to the Mozilla Public License
-     Version 1.1 (the "License"); you may not use this file except in
-     compliance with the License. You may obtain a copy of the License at
-     http://www.mozilla.org/MPL/
-
-     Software distributed under the License is distributed on an "AS IS"
-     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
-     License for the specific language governing rights and limitations
-     under the License.
-
-     The Original Code is ______________________________________.
-
-     The Initial Developer of the Original Code is ________________________.
-     Portions created by ______________________ are Copyright (C) ______
-     _______________________. All Rights Reserved.
-
-     Contributor(s): ______________________________________.
-
-     Alternatively, the contents of this file may be used under the terms
-     of the _____ license (the  "[___] License"), in which case the
-     provisions of [______] License are applicable instead of those
-     above.  If you wish to allow use of your version of this file only
-     under the terms of the [____] License and not to allow others to use
-     your version of this file under the MPL, indicate your decision by
-     deleting  the provisions above and replace  them with the notice and
-     other provisions required by the [___] License.  If you do not delete
-     the provisions above, a recipient may use your version of this file
-     under either the MPL or the [___] License."
-
-     [NOTE: The text of this Exhibit A may differ slightly from the text of
-     the notices in the Source Code files of the Original Code. You should
-     use the text of this Exhibit A rather than the text found in the
-     Original Code Source Code for Your Modifications.]
-
diff --git a/core.network/manifest.mf b/core.network/manifest.mf
index 41db36c..c9b89a7 100644
--- a/core.network/manifest.mf
+++ b/core.network/manifest.mf
@@ -2,5 +2,5 @@ Manifest-Version: 1.0
 OpenIDE-Module: org.netbeans.core.network
 OpenIDE-Module-Localizing-Bundle: org/netbeans/core/network/proxy/Bundle.properties
 OpenIDE-Module-Provides: org.netbeans.core.ProxySettings.Reloader
-OpenIDE-Module-Specification-Version: 1.11
+OpenIDE-Module-Specification-Version: 1.12
 
diff --git a/core.network/nbproject/project.properties b/core.network/nbproject/project.properties
index 6e6e301..a19bad1 100644
--- a/core.network/nbproject/project.properties
+++ b/core.network/nbproject/project.properties
@@ -16,7 +16,7 @@
 # under the License.
 
 is.autoload=true
-javac.source=1.6
+javac.source=1.8
 javac.compilerargs=-Xlint -Xlint:-serial
-release.external/nsProxyAutoConfig-mozilla2.0.js=modules/ext/nsProxyAutoConfig.js
-jnlp.indirect.files=modules/ext/nsProxyAutoConfig.js
+javadoc.arch=${basedir}/arch.xml
+javadoc.apichanges=${basedir}/apichanges.xml
diff --git a/core.network/nbproject/project.xml b/core.network/nbproject/project.xml
index d89cca4..0fe2742 100644
--- a/core.network/nbproject/project.xml
+++ b/core.network/nbproject/project.xml
@@ -26,6 +26,15 @@
             <code-name-base>org.netbeans.core.network</code-name-base>
             <module-dependencies>
                 <dependency>
+                    <code-name-base>org.netbeans.api.annotations.common</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.28</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.netbeans.core</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
@@ -44,6 +53,15 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.netbeans.libs.jna.platform</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.16</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.netbeans.modules.keyring</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
@@ -163,7 +181,14 @@
                     </test-dependency>
                 </test-type>
             </test-dependencies>
-            <public-packages/>
+            <friend-packages>
+                <friend>org.netbeans.core</friend>
+                <package>org.netbeans.core.network.proxy.pac</package>
+                <package>org.netbeans.core.network.proxy.pac.datetime</package>
+                <package>org.netbeans.core.network.proxy.pac.impl</package>
+                <package>org.netbeans.core.network.utils</package>
+                <package>org.netbeans.core.network.utils.hname</package>
+            </friend-packages>
         </data>
     </configuration>
 </project>
diff --git a/core.network/src/org/netbeans/core/network/proxy/Bundle.properties b/core.network/src/org/netbeans/core/network/proxy/Bundle.properties
index 9c2bd8f..bee29a1 100644
--- a/core.network/src/org/netbeans/core/network/proxy/Bundle.properties
+++ b/core.network/src/org/netbeans/core/network/proxy/Bundle.properties
@@ -14,4 +14,6 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-OpenIDE-Module-Name=Core Network Settings
+OpenIDE-Module-Display-Category=Infrastructure
+OpenIDE-Module-Name=Core Network
+OpenIDE-Module-Short-Description=Support for outgoing network connections (ProxySelector, ...)
diff --git a/core.network/src/org/netbeans/core/network/proxy/ProxyAutoConfig.java b/core.network/src/org/netbeans/core/network/proxy/ProxyAutoConfig.java
index 2a6db70..008d80b 100644
--- a/core.network/src/org/netbeans/core/network/proxy/ProxyAutoConfig.java
+++ b/core.network/src/org/netbeans/core/network/proxy/ProxyAutoConfig.java
@@ -18,19 +18,19 @@
  */
 package org.netbeans.core.network.proxy;
 
-import org.netbeans.core.ProxySettings;
 import java.io.*;
 import java.net.*;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.util.*;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import javax.script.Invocable;
-import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
-import javax.script.ScriptException;
-import org.openide.filesystems.FileObject;
-import org.openide.filesystems.FileUtil;
-import org.openide.filesystems.URLMapper;
+import org.netbeans.core.network.proxy.pac.PacParsingException;
+import org.netbeans.core.network.proxy.pac.PacScriptEvaluator;
+import org.netbeans.core.network.proxy.pac.PacScriptEvaluatorFactory;
+import org.netbeans.core.network.proxy.pac.PacScriptEvaluatorNoProxy;
+import org.netbeans.core.network.proxy.pac.PacValidationException;
+import org.openide.util.Lookup;
 import org.openide.util.RequestProcessor;
 import org.openide.util.RequestProcessor.Task;
 import org.openide.util.Utilities;
@@ -43,7 +43,6 @@ public class ProxyAutoConfig {
 
     private static final Map<String, ProxyAutoConfig> file2pac = new HashMap<String, ProxyAutoConfig>(2);
     private static final RequestProcessor RP = new RequestProcessor(ProxyAutoConfig.class);
-    private static final String NS_PROXY_AUTO_CONFIG_URL = "nbinst://org.netbeans.core/modules/ext/nsProxyAutoConfig.js"; // NOI18N
     private static final String PROTO_FILE = "file://";
 
     /**
@@ -66,7 +65,7 @@ public class ProxyAutoConfig {
         return file2pac.get(pacFile);
     }
     private static final Logger LOGGER = Logger.getLogger(ProxyAutoConfig.class.getName());
-    private Invocable inv = null;
+    private PacScriptEvaluator evaluator;
     private final Task initTask;
     private final URI pacURI;
 
@@ -88,45 +87,36 @@ public class ProxyAutoConfig {
     }
 
     private void initEngine() {
-        InputStream pacIS;
-        try {
-            if (pacURI.isAbsolute()) {
-                pacIS = downloadPAC(pacURI.toURL());
-            } else {
-                pacIS = null;
+        String pacSource = null;
+        if (pacURI.isAbsolute()) {
+            try (InputStream is = downloadPAC(pacURI.toURL())) {
+                pacSource = convertInputStreamToString(is, 8192, StandardCharsets.UTF_8);
+            } catch (MalformedURLException ex) {
+                LOGGER.log(Level.INFO, "PAC URL is malformed : ", ex);
+                return;
+            } catch (IOException ex) {
+                LOGGER.log(Level.INFO, "InputStream for " + pacURI + " throws ", ex);
+                return;
             }
-        } catch (IOException ex) {
-            LOGGER.log(Level.INFO, "InputStream for " + pacURI + " throws " + ex, ex);
-            return;
-        }
-        if (pacIS == null) {
-            return ;
         }
-        String utils = downloadUtils();
-        ScriptEngine eng;
-        try {
-            eng = evalPAC(pacIS, utils);
-        } catch (FileNotFoundException ex) {
-            LOGGER.log(Level.FINE, "While constructing ProxyAutoConfig thrown " + ex, ex);
-            return ;
-        } catch (ScriptException ex) {
-            LOGGER.log(Level.FINE, "While constructing ProxyAutoConfig thrown " + ex, ex);
-            return ;
-        } finally {
-            if (pacIS != null) {
-                try {
-                    pacIS.close();
-                } catch (IOException ex) {
-                    LOGGER.log(Level.FINE, "While closing PAC input stream thrown " + ex, ex);
-                }
+                
+        PacScriptEvaluatorFactory factory = Lookup.getDefault().lookup(PacScriptEvaluatorFactory.class);
+        if (factory == null) {
+            LOGGER.log(Level.WARNING, "No PAC Script Evaluator factory found. Will use dummy evaluator instead.");
+            evaluator = new PacScriptEvaluatorNoProxy();
+        } else {
+            try {
+                evaluator = factory.createPacScriptEvaluator(pacSource);
+            } catch (PacParsingException ex) {
+                LOGGER.log(Level.WARNING, "There was a catastrophic error with the PAC script downloaded from " + pacURI + ". Will use dummy instead. Error was : ", ex);
+                evaluator = factory.getNoOpEvaluator();
             }
         }
-        assert eng != null : "JavaScri5pt engine cannot be null";
-        if (eng == null) {
-            LOGGER.log(Level.WARNING, "JavaScript engine cannot be null");
-            return ;
+        
+        assert evaluator != null : "JavaScript evaluator cannot be null";
+        if (evaluator == null) {
+            LOGGER.log(Level.WARNING, "JavaScript evaluator cannot be null");
         }
-        inv = (Invocable) eng;
     }
 
     @SuppressWarnings("unchecked")
@@ -146,24 +136,12 @@ public class ProxyAutoConfig {
                 }
             }
         }
-        if (inv == null) {
-            return Collections.singletonList(Proxy.NO_PROXY);
-        }
-        Object proxies = null;
         try {
-            proxies = inv.invokeFunction("FindProxyForURL", u.toString(), u.getHost()); // NOI18N
-        } catch (ScriptException ex) {
-            LOGGER.log(Level.FINE, "While invoking FindProxyForURL(" + u + ", " + u.getHost() + " thrown " + ex, ex);
-        } catch (NoSuchMethodException ex) {
-            LOGGER.log(Level.FINE, "While invoking FindProxyForURL(" + u + ", " + u.getHost() + " thrown " + ex, ex);
-        }
-        List<Proxy> res = analyzeResult(u, proxies);
-        if (res == null) {
-            LOGGER.info("findProxyForURL(" + u + ") returns null.");
-            res = Collections.emptyList();
+            return evaluator.findProxyForURL(u);
+        } catch (PacValidationException ex) {
+            LOGGER.log(Level.WARNING, "Incorrect answer from PAC script : ", ex);
+            return Collections.singletonList(Proxy.NO_PROXY);
         }
-        LOGGER.fine("findProxyForURL(" + u + ") returns " + Arrays.asList(res));
-        return res;
     }
 
     private static InputStream downloadPAC (URL pacURL) throws IOException {
@@ -173,142 +151,27 @@ public class ProxyAutoConfig {
         return is;
     }
 
-    private static ScriptEngine evalPAC(InputStream is, String utils) throws FileNotFoundException, ScriptException {
-        ScriptEngineManager factory = new ScriptEngineManager();
-        ScriptEngine engine = factory.getEngineByName("JavaScript");
-        Reader pacReader = new InputStreamReader(is);
-        engine.eval(pacReader);
-        engine.eval(utils);
-        return engine;
-    }
 
-    private List<Proxy> analyzeResult(URI uri, Object proxiesString) {
-        if (proxiesString == null) {
-            LOGGER.fine("Null result for " + uri);
-            return null;
-        }
-        Proxy.Type proxyType;
-        String protocol = uri.getScheme();
-        assert protocol != null : "Invalid scheme of uri " + uri + ". Scheme cannot be null!";
-        if (protocol == null) {
-            return null;
-        } else {
-            if (("http".equals(protocol)) || ("https".equals(protocol))) { // NOI18N
-                proxyType = Proxy.Type.HTTP;
-            } else {
-                proxyType = Proxy.Type.SOCKS;
-            }
-        }
-        StringTokenizer st = new StringTokenizer(proxiesString.toString(), ";"); //NOI18N
-        List<Proxy> proxies = new LinkedList<Proxy>();
-        while (st.hasMoreTokens()) {
-            String proxy = st.nextToken();
-            if (ProxySettings.DIRECT.equals(proxy.trim())) {
-                proxies.add(Proxy.NO_PROXY);
-            } else {
-                String host = getHost(proxy);
-                Integer port = getPort(proxy);
-                if (host != null && port != null) {
-                    proxies.add(new Proxy(proxyType, new InetSocketAddress(host, port)));
-                }
-            }
-        }
-        return proxies;
-    }
 
-    private static String getHost(String proxy) {
-        if (proxy.startsWith("PROXY ")) {
-            proxy = proxy.substring(6);
+    protected static String convertInputStreamToString(InputStream in, int initSize, Charset charset) throws IOException {
+        ByteArrayOutputStream buf = new ByteArrayOutputStream(initSize);
+        byte[] buffer = new byte[1024];
+        int length;
+        while ((length = in.read(buffer)) != -1) {
+            buf.write(buffer, 0, length);
         }
-        int i = proxy.lastIndexOf(":"); // NOI18N
-        if (i <= 0 || i >= proxy.length() - 1) {
-            LOGGER.info("No port in " + proxy);
-            return null;
-        }
-
-        String host = proxy.substring(0, i);
-
-        return ProxySettings.normalizeProxyHost(host);
+        return buf.toString(charset.name());
     }
 
-    private static Integer getPort(String proxy) {
-        if (proxy.startsWith("PROXY ")) {
-            proxy = proxy.substring(6);
-        }
-        int i = proxy.lastIndexOf(":"); // NOI18N
-        if (i <= 0 || i >= proxy.length() - 1) {
-            LOGGER.info("No port in " + proxy);
-            return null;
-        }
-
-        String port = proxy.substring(i + 1);
-        if (port.indexOf('/') >= 0) {
-            port = port.substring(0, port.indexOf('/'));
-        }
-
-        try {
-            return Integer.parseInt(port);
-        } catch (NumberFormatException ex) {
-            LOGGER.log(Level.INFO, ex.getLocalizedMessage(), ex);
-            return null;
-        }
-    }
-
-    private static String downloadUtils() {
-        StringBuilder builder = new StringBuilder();
-        BufferedReader reader = null;
-        // XXX why is the below not more simply:
-        // reader = new BufferedReader(new URL(NS_PROXY_AUTO_CONFIG_URL).openStream());
-        FileObject fo = null;
-        try {
-            try {
-                fo = URLMapper.findFileObject(new URL(NS_PROXY_AUTO_CONFIG_URL));
-            } catch (MalformedURLException ex) {
-                LOGGER.log(Level.INFO, ex.getMessage(), ex);
-                return "";
-            }
-            reader = new BufferedReader(new java.io.FileReader(FileUtil.toFile(fo)));
-        } catch (FileNotFoundException ex) {
-            LOGGER.log(Level.INFO, ex.getMessage(), ex);
-        }
-        try {
-            String line;
-            boolean doAppend = false;
-            while ((line = reader.readLine()) != null) {
-                line = line.trim();
-                if( line.startsWith("var pacUtils =") ) { //NOI18N
-                    doAppend = true;
-                    continue;
-                }
-                if( !doAppend )
-                    continue;
-                if (line.endsWith("+")) { // NOI18N
-                    line = line.substring(0, line.length() - 1);
-                }
-                builder.append(line.replaceAll("\"", "").replaceAll("\\\\n", "").replaceAll("\\\\\\\\", "\\\\")); // NOI18N
-                builder.append(System.getProperty("line.separator")); // NOI18N
-            }
-        } catch (IOException ex) {
-            LOGGER.log(Level.INFO, "While downloading nsProxyAutoConfig.js thrown " + ex.getMessage(), ex);
-        } finally {
-            if (reader != null) {
-                try {
-                    reader.close();
-                } catch (IOException ex) {
-                    LOGGER.log(Level.FINE, ex.getMessage(), ex);
-                }
-            }
-        }
-        return builder.toString();
-    }
 
     private String normalizePAC(String pacURL) {
         int index;
-        if ((index = pacURL.indexOf("\n")) != -1) { // NOI18N
-            pacURL = pacURL.substring(0, index);
+        String inputSanitized = pacURL;
+        if ((index = inputSanitized.indexOf("\n")) != -1) { // NOI18N
+            inputSanitized = inputSanitized.substring(0, index);
         }
-        if ((index = pacURL.indexOf("\r")) != -1) { // NOI18N
-            pacURL = pacURL.substring(0, index);
+        if ((index = inputSanitized.indexOf("\r")) != -1) { // NOI18N
+            inputSanitized = inputSanitized.substring(0, index);
         }
         String fileLocation = pacURL;
         if (fileLocation.startsWith(PROTO_FILE)) {
@@ -316,13 +179,13 @@ public class ProxyAutoConfig {
         }
         File f = new File(fileLocation);
         if (f.canRead()) {
-            pacURL = Utilities.toURI(f).toString();
+            inputSanitized = Utilities.toURI(f).toString();
         } else {
-            pacURL = pacURL.replaceAll("\\\\", "/"); //NOI18N
+            inputSanitized = inputSanitized.replaceAll("\\\\", "/"); //NOI18N
         }
-        if ((index = pacURL.indexOf(" ")) != -1) { // NOI18N
-            pacURL = pacURL.substring(0, index);
+        if ((index = inputSanitized.indexOf(" ")) != -1) { // NOI18N
+            inputSanitized = inputSanitized.substring(0, index);
         }
-        return pacURL.trim();
+        return inputSanitized.trim();
     }
 }
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethods.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethods.java
new file mode 100644
index 0000000..eb08624
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethods.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+
+
+/**
+ * Stub for PAC Helpers. 
+ * 
+ * <p>
+ * The 'Helpers' are utility functions that the PAC script can make use of.
+ * 
+ * @author lbruun
+ */
+public abstract class PacHelperMethods implements PacHelperMethodsNetscape, PacHelperMethodsMicrosoft {
+ 
+    
+    /**
+     * Writes something to log or elsewhere. This allows the PAC script to do
+     * simple logging for debugging purpose. It overwrites the standard
+     * JavaScript {@code alert()} function.
+     *
+     * <p>
+     * Note that the JavaScript {@code alert()} function should not be used in
+     * a production PAC script. It is only intended for debugging and
+     * unit test purpose.
+     *
+     * <p>
+     * The default implementation simply writes to stderr.
+     *
+     * @param message text to log 
+     */
+    public void alert(String message) {
+        System.err.println(message);
+    }
+
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethodsMicrosoft.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethodsMicrosoft.java
new file mode 100644
index 0000000..f9cef13
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethodsMicrosoft.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+/**
+ * Microsoft's extensions to complement Netscape's original PAC script 
+ * helper functions. (as defined in {@link PacHelperMethodsNetscape}).
+ * 
+ * <p>
+ * The JavaScript helper functions are defined in Java terms by this interface.
+ * The Java methods are named exactly as their JavaScript counterparts.
+ * 
+ * <p>
+ * Microsoft has added these methods in recognition that the original
+ * Netscape functions were defined at a time before IPv6 became widely used
+ * or was even finalized. The original Netscape functions simply didn't take
+ * IPv6 into account and should therefore be interpreted in an IPv4-only context.
+ * Support for these new functions were first introduced in Internet Explorer v7.
+ * Other browsers, e.g. Chrome, have since then implemented them too.
+ * 
+ * <p>
+ * Microsoft also defined a new entry-point function, {@code FindProxyForURLEx(url, host)},
+ * which should replace the legacy {@code FindProxyForURL(url, host)} function. 
+ * Microsoft only makes the new helper functions available from this new entry
+ * function. However, Chrome does it differently in that the new helper
+ * functions are indeed available, but there's no support for the new entry function, 
+ * {@code FindProxyForURLEx}, meaning the new helper functions are
+ * available from the old entry method, {@code FindProxyForURL}.
+ * An implementation of {@link PacScriptEvaluator} should strive for maximum
+ * compatibility meaning all helper functions should be available regardless
+ * of entry point.
+ * 
+ * @author lbruun
+ */
+public interface PacHelperMethodsMicrosoft {
+
+    /**
+     * Tries to resolve the hostname. Returns true if succeeds. Unlike the
+     * original Netscape function, {@code isResolvable()}, this function supports
+     * both IPv4 and IPv6 addresses, meaning it should return {@code true} if the 
+     * input can be resolved to any type of IP address.
+     * 
+     * <p>
+     * Examples of usage from JavaScript:
+     * <pre>
+     *   isResolvable("www.netscape.com")
+     *     is true (unless DNS fails to resolve it due to a firewall or some other reason).
+     *   isResolvable("bogus.domain.foobar")
+     *     is false. 
+     * </pre>
+     * @param host
+     * @return 
+     */
+    public boolean isResolvableEx(String host);
+
+    /**
+     * Resolves the given host name into its address or addresses.
+     * 
+     * <p>
+     * Example of usage from JavaScript:
+     * <pre>
+     *   dnsResolveEx("testmachine1");
+     *      returns the string "2001:4898:28:7:982d:a3b3:97ad:7dd0;192.168.1.99"
+     * </pre>
+     * @param host
+     * @return a semi-colon delimited string containing (potentially both)
+     *    IPv6 and IPv4 addresses or an empty string if host is not resolvable
+     */
+    public String dnsResolveEx(String host);
+
+    /**
+     * Returns the IP addresses the local host is known by.
+     * 
+     * <p>
+     * Example of usage from JavaScript:
+     * <pre>
+     *   myIpAddressEx()
+     *     would return the string "2001:4898:28:7:982d:a3b3:97ad:7dd0;198.95.249.79" 
+     *     if you were running the application on that host. 
+     * </pre>
+     * 
+     * @return a semi-colon delimited string containing (potentially both)
+     *    IPv6 and IPv4 addresses.
+     */
+    public String myIpAddressEx();
+
+    /**
+     * Sorts a list of IP addresses in ascending order. If both IPv6 and IPv4 
+     * addresses are passed as input to this function, then the sorted IPv6
+     * addresses are followed by the sorted IPv4 addresses.
+     *
+     * <p>
+     * Example of usage from JavaScript:
+     * <pre>
+     *   sortIpAddressList("10.2.3.9;2001:4898:28:3:201:2ff:feea:fc14;::1;127.0.0.1;::9");
+     *           returns   "::1;::9;2001:4898:28:3:201:2ff:feea:fc14;10.2.3.9;127.0.0.1";
+     * </pre>
+     * 
+     * <p>
+     * NOTE: Microsoft Internet Explorer 11 (tested on 11.608.15063.0) doesn't
+     * give correct results on this function. For example the result of
+     * {@code sortIpAddressList("10.2.3.9;2001:4898:28:3:201:2ff:feea:fc14;::1;127.0.0.1;::9")}
+     * is
+     * {@code "[::1];10.2.3.9;127.0.0.1;[2001:4898:28:3:201:2ff:feea:fc14];[::9]"}
+     * which is incorrect in more ways than one. In contrast, Chrome does it as
+     * expected and as in the example above. In summary the following problems
+     * are seen with the IE 11 implementation of this function:
+     * <ul>
+     *   <li>IPv4 addresses are not in sort order <i>after</i> after
+     *       the IPv6 addresses as the example shows. The documentation
+     *       clearly states the opposite.
+     *       </li>
+     *   <li>IPv4-mapped IPv6 address (e.g. {@code fe80::5efe:157.59.139.22} 
+     *       doesn't seem to be working. This is strange as it is used in the 
+     *       example in <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/gg308482(v=vs.85).aspx">Microsoft's own documentation</a>. 
+     *       Such an IP literal seems to throw an error which means
+     *       the PAC script will not return a result, which means IE will choose 
+     *       its default option, namely "DIRECT".
+     *       </li>
+     *   <li>IPv6 literals in the output will be enclosed in [], e.g. 
+     *       {@code "1080:0:0:0:8:800:200c:417a"} in the input will become 
+     *       {@code "[1080:0:0:0:8:800:200c:417a]"} in the output.
+     *       This too is in contrast to the documentation.
+     *       </li>
+     * </ul>
+     * <br>
+     * 
+     * @param ipAddressList a semi-colon delimited string containing IP addresses
+     * @return a list of sorted semi-colon delimited IP addresses or an empty 
+     *     string if unable to sort the IP address list.
+     */
+    public String sortIpAddressList(String ipAddressList);
+    
+    /**
+     * Gets the version of the PAC script processing engine.
+     * 
+     * <p>
+     * Microsoft added this function to allow IT administrators to update 
+     * their PAC scripts to use different versions of the PAC processing engine 
+     * without causing breaks to their existing deployment. 
+     * For example, if Microsoft added a function to the 2.0 version of the the
+     * Microsoft PAC processing engine, then administrators can check the 
+     * version before attempting to call that function. This allows their 
+     * script to work with client running versions 1.0 and 2.0 of the engine.
+     * 
+     * <p>
+     * Note to implementers: Currently this function should simply return "1.0"
+     * in order retain compatibility with Microsoft world.
+     * 
+     * @return 
+     */
+    public String getClientVersion();
+    
+    /**
+     * True if the IP address of the host matches the specified IP address
+     * pattern.
+     * 
+     * <p> 
+     * Microsoft's documentation is vague / contradictory on what the pattern
+     * argument, {@code ipPrefix}, is, meaning is it a <i>list</i> of patterns 
+     * or is it a singular pattern?. We err on the side of caution and decide that
+     * the {@code ipPrefix} is a semi-colon separated list of patterns and that
+     * the method must return {@code true} if the supplied {@code ipAddress}
+     * matches <i>any</i> of the patterns in the list.
+     * 
+     * <p> 
+     * Similarly, the interpretation of the first argument, {@code host},
+     * is open for discussion. In the sibling function, {@code isInNet}, it
+     * is clearly stated that this argument can be either a host name or a 
+     * literal IP address. But Microsoft's documentation on {@code isInNetEx}
+     * limits the argument to only being a literal IP address. Again we err
+     * on the side of caution and decide that this argument can be either. If
+     * it is a host name then the method must do a name service lookup first
+     * (to convert to IP), before the actual comparison can begin.
+     * 
+     * 
+     * <p>
+     * Examples of usage from JavaScript:
+     * <pre>
+     *   isInNetEx(ipAddress, "198.95.249.79/32");
+     *     true if the IP address of host matches exactly 198.95.249.79
+     *   isInNetEx(ipAddress, "198.95.0.0/16");
+     *     true if the IP address of the host matches 198.95.&#42;.&#42;
+     *   isInNetEx(ipAddress, "3ffe:8311:ffff/48");
+     *     true if the IP address of the host matches 3ffe:8311:fff:&#42;:&#42;:&#42;:&#42;:&#42;
+     *   isInNetEx(ipAddress, "198.95.249.0/24;198.122.0.0/16");
+     *     true if the IP address of host matches 198.95.249.&#42; or matches 198.122.&#42;.&#42;
+     * </pre>
+     *
+     * @param host a string containing an IPv6/IPv4 addresses or a host name.
+     * @param ipPrefix a string containing semi-colon delimited IP prefixes with 
+     *    top n bits specified in the bit field 
+     *    (i.e. 3ffe:8311:ffff::/48 or 123.112.0.0/16).
+     * @return {@code true} if the IP address matches the specified IP address
+     *    pattern ({code ipPrefix}). Also returns {@code false} if the prefix 
+     *    is not in the correct format or if addresses and prefixes of different 
+     *    types are used in the comparison (i.e. IPv4 prefix and an IPv6 address).
+     */
+    public boolean isInNetEx(String host, String ipPrefix);
+
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethodsNetscape.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethodsNetscape.java
new file mode 100644
index 0000000..8531e98
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacHelperMethodsNetscape.java
@@ -0,0 +1,374 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+/**
+ * Netscape's original PAC script helper functions. In their original
+ * document, Netscape defined 12 helper functions which should be available
+ * to the JavaScript PAC. The first browser to implement these were - unsurpringsly -
+ * the Netscape Navigator.
+ * 
+ * <p>
+ * The JavaScript helper functions are defined in Java terms by this interface.
+ * The Java methods are named exactly as their JavaScript counterparts.
+ * 
+ * <p>
+ * The documentation on each of the methods in the interface is more
+ * or less copied verbatim from Netscape's original documentation. 
+ * 
+ * <p>
+ * Beware that at the time when Netscape defined these functions IPv6 
+ * wasn't on the radar. Therefore the concensus among browser makers
+ * is that IPv4 is implied in all these functions.
+ * 
+ * @author lbruun
+ */
+public interface PacHelperMethodsNetscape {
+
+    /**
+     * True if there is no domain name in the hostname (no dots). 
+     * 
+     * <p>
+     * Examples:
+     * <pre>
+     *   isPlainHostName("www")
+     *     is true.
+     *   isPlainHostName("www.netscape.com")
+     *     is false.
+     * </pre>
+     * @param host host name
+     * @return 
+     */
+    public boolean isPlainHostName(String host);
+
+    /**
+     * Returns true if the domain of hostname matches. 
+     * 
+     * <p>
+     * Examples:
+     * <pre>
+     *   dnsDomainIs("www.netscape.com", ".netscape.com")
+     *     is true.
+     *   dnsDomainIs("www", ".netscape.com")
+     *     is false.
+     *   dnsDomainIs("www.mcom.com", ".netscape.com")
+     *     is false.
+     * </pre>
+     * @param host the host name from the URL.
+     * @param domain the domain name to test the host name against.
+     * @return 
+     */
+    public boolean dnsDomainIs(String host, String domain);
+
+    /**
+     * Is true if the hostname matches exactly the specified hostname, or if
+     * there is no domain name part in the hostname, but the unqualified
+     * hostname matches.
+     *
+     * <p>
+     * Examples of usage from JavaScript:
+     * <pre>
+     *   localHostOrDomainIs("www.netscape.com", "www.netscape.com")
+     *     is true (exact match).
+     *   localHostOrDomainIs("www", "www.netscape.com")
+     *     is true (hostname match, domain not specified).
+     *   localHostOrDomainIs("www.mcom.com", "www.netscape.com")
+     *     is false (domain name mismatch).
+     *   localHostOrDomainIs("home.netscape.com", "www.netscape.com")
+     *     is false (hostname mismatch).
+     * </pre>
+     * 
+     * @param host the hostname from the URL.
+     * @param hostdom fully qualified hostname to match against.
+     * @return 
+     */
+    public boolean localHostOrDomainIs(String host, String hostdom);
+
+    /**
+     * Tries to resolve the hostname. Returns true if succeeds. Strictly
+     * speaking - and in the spirit of the original Netscape specification -
+     * this method should only return {@code true} if the argument can be
+     * resolved into an IPv4 address.
+       * 
+     * <p>
+     * Examples of usage from JavaScript:
+     * <pre>
+     *   isResolvable("www.netscape.com")
+     *     is true (unless DNS fails to resolve it due to a firewall or some other reason).
+     *   isResolvable("bogus.domain.foobar")
+     *     is false. 
+     * </pre>
+     * @param host
+     * @return 
+     */
+    public boolean isResolvable(String host);
+
+    /**
+     * Resolves the given DNS hostname into an IPv4 address, 
+     * and returns it in the dot separated format as a string. 
+     * 
+     * <p>
+     * Example of usage from JavaScript:
+     * <pre>
+     *   dnsResolve("home.netscape.com")
+     *     returns the string "198.95.249.79". 
+     * </pre>
+     * @param host
+     * @return 
+     */
+    public String dnsResolve(String host);
+
+    /**
+     * Returns the IPv4 address of the host that the application is running on, 
+     * as a string in the dot-separated integer format.
+     * 
+     * <p>
+     * Example of usage from JavaScript:
+     * <pre>
+     *   myIpAddress()
+     *     would return the string "198.95.249.79" if you were running the 
+     *     the application on that host. 
+     * </pre>
+     * 
+     * @return IPv4 address in textual form
+     */
+    public String myIpAddress();
+
+    /**
+     * True if the IP address of the host matches the specified IP address
+     * pattern.
+     * 
+     * <p>
+     * Examples of usage from JavaScript:
+     * <pre>
+     *   isInNet(host, "198.95.249.79", "255.255.255.255")
+     *     is true if the IP address of host matches exactly 198.95.249.79.
+     *   isInNet(host, "198.95.0.0", "255.255.0.0")
+     *     is true if the IP address of the host matches 198.95.&#42;.&#42;. 
+     * </pre>
+     *
+     * @param host a DNS hostname or IPv4 address. If a hostname is passed, it
+     * will be resolved into an IP address by this function.
+     * @param pattern an IPv4 address pattern in the dot-separated format.
+     * @param mask mask for the IP address pattern informing which parts of the
+     * IP address should be matched against. 0 means ignore, 255 means match.
+     * @return
+     */
+    public boolean isInNet(String host, String pattern, String mask);
+
+    /**
+     * Returns the number of DNS domain levels (number of dots) 
+     * in the hostname. 
+     * 
+     * <p>
+     * Examples of usage from JavaScript:
+     * <pre>
+     *   dnsDomainLevels("www")
+     *     returns 0.
+     *   dnsDomainLevels("www.netscape.com")
+     *     returns 2. 
+     * </pre>
+     * 
+     * @param host hostname
+     * @return 
+     */
+    public int dnsDomainLevels(String host);
+
+    /**
+     * Returns true if the string matches the specified shell expression.
+     * Note that the argument is a <i>shell expression</i>, not a
+     * regular expression. 
+     * 
+     * <p>
+     * Examples of usage from JavaScript:
+     * <pre>
+     *   shExpMatch("http://home.netscape.com/people/ari/index.html", "&#42;/ari/&#42;")
+     *     is true.
+     *   shExpMatch("http://home.netscape.com/people/montulli/index.html", "&#42;/ari/&#42;")
+     *     is false.
+     * </pre>
+     * @param str
+     * @param shexp shell expression
+     * @return 
+     */
+    public boolean shExpMatch(String str, String shexp);
+
+    /**
+     * Tests if current day is within a day-of-week range.
+     * 
+     * <p>
+     * There are several forms of this method from JavaScript:
+     * <pre>
+     *    weekdayRange(wd1)
+     *    weekdayRange(wd1, gmt)
+     *    weekdayRange(wd1, wd2)
+     *    weekdayRange(wd1, wd2, gmt)
+     * </pre>
+     * 
+     * Parameters:
+     * <ul>
+     *    <li>{@code wd1} and {@code wd2} are weekday specifications. </li>
+     *    <li>{@code gmt} is either the string "GMT", which makes time comparison 
+     *         occur in GMT timezone; or if not present, times are taken to 
+     *         be in the local timezone. If this parameter exists it 
+     *         must always be the last parameter.
+     *    </li>
+     * </ul>     
+     * 
+     * <p>If only {@code wd1} is present, the function yields a true value on the
+     * weekday that the parameter represents. If both {@code wd1} and
+     * {@code wd2} are specified, the condition is true if the current weekday
+     * is in between those two weekdays. Bounds are inclusive.
+     *
+     * <p>The weekday abbreviations used in {@code wd1} and {@code wd2} must be 
+     * one of the following:
+     * <pre>
+     *    MON  TUE  WED  THU  FRI  SAT  SUN
+     * </pre>
+     * 
+     * <p>
+     * Examples of usage from JavaScript:
+     * <pre>
+     *   weekdayRange("MON", "FRI")
+     *     Returns true Monday through Friday (local time zone).
+     *   weekdayRange("MON", "FRI", "GMT")
+     *     Returns true Monday through Friday, in Greenwich Mean Time.
+     *   weekdayRange("SAT")
+     *     Returns true on Saturdays, local time.
+     *   weekdayRange("SAT", "GMT")
+     *     Returns true on Saturdays, in Greenwich Mean Time.
+     *   weekdayRange("FRI", "MON") 
+     *     Returns true Friday through Monday (the order is important)
+     *   weekdayRange("WED", "TUE") 
+     *     Returns true always, all 7 days a week (although an alien way of specifying it)
+     * </pre>
+     * <br>
+     * @param args up to max 3 arguments
+     * @return 
+     * @see org.netbeans.network.proxy.pac.datetime.PacUtilsDateTime#WEEKDAY_NAMES
+     */
+    public boolean weekdayRange(Object... args);
+
+    /**
+     * Tests if current date is within a date range.
+     * 
+     * <p>There are several forms of this method from JavaScript:
+     * <pre>
+     *   dateRange(day)
+     *   dateRange(day1, day2)
+     *   dateRange(mon)
+     *   dateRange(month1, month2)
+     *   dateRange(year)
+     *   dateRange(year1, year2)
+     *   dateRange(day1, month1, day2, month2)
+     *   dateRange(month1, year1, month2, year2)
+     *   dateRange(day1, month1, year1, day2, month2, year2)
+     *   dateRange(day1, month1, year1, day2, month2, year2, gmt)
+     * </pre>
+     * Even if not shown above, the {@code gmt} parameter can always be
+     * added as an (optional) last parameter.
+     * 
+     * <p>
+     * <ul>
+     *    <li>{@code day} is the day of month between 1 and 31 (as an integer).
+     *    </li>
+     *    <li>{@code month} is one of the month strings: {@code JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC}
+     *    </li>
+     *    <li>{@code year} is the full year number with 4 digits, for example 1995. (as an integer)
+     *    <li>{@code gmt} is either the string "GMT", which makes time comparison 
+     *         occur in GMT timezone; or if not present, times are taken to 
+     *         be in the local timezone. If this parameter exists it 
+     *         must always be the last parameter.
+     *    </li>
+     * </ul>
+     * <br>
+     * <p>
+     * Examples of usage from JavaScript:
+     * <pre>
+     *   dateRange(1)
+     *     true on the first day of each month, local timezone.
+     *   dateRange(1, "GMT")
+     *     true on the first day of each month, GMT timezone.
+     *   dateRange(1, 15)
+     *     true on the first half of each month.
+     *   dateRange(24, "DEC")
+     *     true on 24th of December each year.
+     *   dateRange(24, "DEC", 1995)
+     *     true on 24th of December, 1995.
+     *   dateRange("JAN", "MAR")
+     *     true on the first quarter of the year.
+     *   dateRange(1, "JUN", 15, "AUG")
+     *     true from June 1st until August 15th, each year (including June 1st and August 15th).
+     *   dateRange(1, "JUN", 15, 1995, "AUG", 1995)
+     *     true from June 1st, 1995, until August 15th, same year.
+     *   dateRange("OCT", 1995, "MAR", 1996)
+     *     true from October 1995 until March 1996 (including the entire month of October 1995 and March 1996).
+     *   dateRange(1995)
+     *     true during the entire year 1995.
+     *   dateRange(1995, 1997)
+     *     true from beginning of year 1995 until the end of year 1997.
+     * </pre>
+     * 
+     * @see org.netbeans.network.proxy.pac.datetime.PacUtilsDateTime#MONTH_NAMES
+     * @param args
+     * @return 
+     */
+    public boolean dateRange(Object... args);
+
+    /**
+     * Tests if current time is within a time range.
+     * 
+     * <p>There are several forms of this method from JavaScript:
+     * <pre>
+     *   timeRange(hour)
+     *   timeRange(hour1, hour2)
+     *   timeRange(hour1, min1, hour2, min2)
+     *   timeRange(hour1, min1, sec1, hour2, min2, sec2)
+     *   timeRange(hour1, min1, sec1, hour2, min2, sec2, gmt)
+     * </pre>
+     * Even if not shown above, the {@code gmt} parameter can always be
+     * added as an (optional) last parameter.
+     * <p>
+     * <ul>
+     *    <li>{@code hour} is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.)
+     *    </li>
+     *    <li>{@code min} minutes from 0 to 59.
+     *    </li>
+     *    <li>{@code sec} seconds from 0 to 59.
+     *    <li>{@code gmt} is either the string "GMT", which makes time comparison 
+     *         occur in GMT timezone; or if not present, times are taken to 
+     *         be in the local timezone. If this parameter exists it 
+     *         must always be the last parameter.
+     *    </li>
+     * </ul>
+     * <br>
+     * <p>
+     * Examples of usage from JavaScript:
+     * <pre>
+     *   timeRange(12, 13)
+     *     This statement is true from noon to 1:00 p.m.
+     *   timeRange(12, "GMT")
+     *      This statement is true noon to 12:59 p.m. GMT.
+     *   timeRange(9, 17)
+     *     This statement is true from 9:00 a.m. to 5:00 p.m.
+     *   timeRange(0, 0, 0, 0, 0, 30) 
+     *     true between midnight and 30 seconds past midnight.
+     * </pre>
+     * 
+     * @param args anywhere between 1 and 7 arguments
+     * @return 
+     */
+    public boolean timeRange(Object... args);
+
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacJsEntryFunction.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacJsEntryFunction.java
new file mode 100644
index 0000000..6a4da3b
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacJsEntryFunction.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+/**
+ * Entry points for PAC script.
+ * 
+ * @author lbruun
+ */
+public enum PacJsEntryFunction {
+   
+    /**
+     * Main entry point to JavaScript PAC script as defined by Netscape.
+     * This is JavaScript function name {@code FindProxyForURL()}.
+     */
+    STANDARD("FindProxyForURL"),
+
+    /**
+     * Main entry point to JavaScript PAC script for IPv6 support, 
+     * as defined by Microsoft. 
+     * This is JavaScript function name {@code FindProxyForURLEx()}.
+     */
+    IPV6_AWARE("FindProxyForURLEx");
+    
+    private final String jsFunctionName;
+    
+    PacJsEntryFunction(String jsFunctionName) {
+        this.jsFunctionName = jsFunctionName;    
+    }
+
+    /**
+     * Gets name of JavaScript function.
+     * @return 
+     */
+    public String getJsFunctionName() {
+        return jsFunctionName;
+    }
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacParsingException.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacParsingException.java
new file mode 100644
index 0000000..d7b480b
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacParsingException.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+/**
+ * Thrown when there are syntactical errors in the PAC script.
+ * 
+ * @see PacValidationException
+ * 
+ * @author lbruun
+ */
+public class PacParsingException extends Exception {
+
+   public PacParsingException(Exception ex) {
+        super(ex);
+    }
+     
+   public PacParsingException(String message, Exception ex) {
+        super(message, ex);
+    }
+   
+   public PacParsingException(String message) {
+        super(message);
+    }
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluator.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluator.java
new file mode 100644
index 0000000..ad3d755
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluator.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+import java.net.Proxy;
+import java.net.URI;
+import java.util.List;
+
+/**
+ * PAC Script evaluator.
+ * 
+ * <p>This is a bridge between Java and the PAC script, which is implemented in 
+ * JavaScript. {@code PacScriptEvaluator}s are created via 
+ * {@link PacScriptEvaluatorFactory}.
+ * 
+ * @author lbruun
+ */
+public interface PacScriptEvaluator {
+    
+    /**
+     * Returns the proxy/proxies appropriate to use for the given URI.
+     * 
+     * <p>
+     * The method calls the JavaScript {@code FindProxyForURL(url, host)}
+     * function in the PAC script (or alternatively the
+     * {@code FindProxyForURLEx(url, host)} function), parses the result and
+     * returns it as a prioritized list of proxies.
+     * 
+     * @param uri URI to get proxies for. 
+     * @return
+     * @throws PacValidationException when the result from the JavaScript function
+     *    cannot be interpreted.
+     */
+    public List<Proxy> findProxyForURL(URI uri) throws PacValidationException ;
+    
+    /**
+     * Returns if the Evaluator uses result caching or not. Result caching speeds
+     * up execution as a call to Java method {@link #findProxyForURL(java.net.URI)} will
+     * not result in a call to JavaScript function  {@code FindProxyForURL}
+     * if the URL has been resolved previously. But the Evaluator cannot use
+     * caching if the PAC Script uses methods which depends on time and the Evaluator
+     * may therefore have decided to turn off result caching.
+     * 
+     * <p>
+     * If an implementation of {@code PacScriptEvaluator} never uses result caching 
+     * (because it simply isn't implemented) then this method will always return 
+     * {@code false}.
+     * 
+     * @return 
+     */
+    public boolean usesCaching();
+
+
+    /**
+     * Gets the entry function to the PAC script which the engine uses. 
+     * @return name of JavaScript function
+     */
+    public String getJsEntryFunction();
+    
+    /**
+     * Gets relevant information about the engine, typically the name
+     * of the JavaScript engine (e.g. 'Nashorn'), version number, etc.
+     * @return info
+     */
+    public String getEngineInfo();
+    
+    /**
+     * Gets the JavaScript source code of the PAC script.
+     * @return source code
+     */
+    public String getPacScriptSource();
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluatorFactory.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluatorFactory.java
new file mode 100644
index 0000000..d7a703f
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluatorFactory.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+/**
+ * Creates PAC Script evaluator.
+ * 
+ * @author lbruun
+ */
+public interface PacScriptEvaluatorFactory {
+    
+    /**
+     * Creates a PAC evaluator based on the given JavaScript source code.
+     * 
+     * <p>
+     * The evaluator must treat the {@code pacSource} as untrusted and evaluate
+     * it in a sandbox.
+     * 
+     * <p>
+     * The method will throw {@link PacParsingException} if the JavaScript input
+     * cannot be parsed/interpreted. In this case you may opt to either do
+     * nothing, report to logging system or use a 
+     * {@link #getNoOpEvaluator() no-op evaluator} as an alternative.
+     * 
+     * @param pacSource The JavaScript source code of the PAC script, as a string.
+     *    To be a correct PAC script, it must implement the 
+     *    {@code FindProxyForURL(url, host)} function. Most often the PAC script
+     *    is downloaded from a network location.
+     * @return
+     * @throws PacParsingException if the source code cannot be parsed
+     */
+    public PacScriptEvaluator createPacScriptEvaluator(String pacSource) throws PacParsingException;
+    
+    /**
+     * Gets a no-op PAC evaluator, meaning one which always returns {@code Proxy.NO_PROXY}
+     * for any call to {@link PacScriptEvaluator#findProxyForURL(java.net.URI)}
+     * 
+     * @return 
+     */
+    public PacScriptEvaluator getNoOpEvaluator();
+    
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluatorNoProxy.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluatorNoProxy.java
new file mode 100644
index 0000000..047db8c
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacScriptEvaluatorNoProxy.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+import java.net.Proxy;
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * PAC Script Evaluator which can be used as a last resort. 
+ * Always returns {@code NO_PROXY}.
+ * 
+ * @author lbruun
+ */
+public class PacScriptEvaluatorNoProxy implements PacScriptEvaluator {
+
+    @Override
+    public List<Proxy> findProxyForURL(URI u) {
+        return Collections.singletonList(Proxy.NO_PROXY);
+    }
+
+    @Override
+    public boolean usesCaching() {
+        return false;
+    }
+
+    @Override
+    public String getJsEntryFunction() {
+        return "NONE";
+    }
+
+    @Override
+    public String getEngineInfo() {
+        return "Dummy engine. Always returns NO_PROXY";
+    }
+
+    @Override
+    public String getPacScriptSource() {
+        return "function FindProxyForURL(url, host) {\n"
+           +   "    return \"DIRECT\";\n"
+           +   "};";
+    }
+    
+    
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacUtils.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacUtils.java
new file mode 100644
index 0000000..c7b8a2f
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacUtils.java
@@ -0,0 +1,356 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+import java.math.BigInteger;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import org.netbeans.core.network.utils.IpAddressUtils;
+import org.netbeans.core.network.utils.SimpleObjCache;
+
+/**
+ * Methods and constants useful in PAC script evaluation.
+ * 
+ * @see org.netbeans.network.proxy.pac.datetime.PacUtilsDateTime
+ * 
+ * @author lbruun
+ */
+public class PacUtils {
+
+    /**
+     * Size of the cache used for precompiled GLOBs.
+     */
+    public static final int PRECOMPILED_GLOB_CACHE_MAX_ITEMS = 10;
+    private static final SimpleObjCache<String, Pattern> PRECOMPILED_GLOB_CACHE
+            = new SimpleObjCache<>(PRECOMPILED_GLOB_CACHE_MAX_ITEMS);
+
+
+    
+
+    /**
+     * Translate a GLOB pattern into a RegExp pattern. GLOB patterns originate
+     * from Unix hosts where they are primarily used for file pattern matching.
+     * In the original PAC specification from Netscape a GLOB pattern is
+     * referred to as a 'shell expression'.
+     *
+     * <p>
+     * This method supports all GLOB wildcards, such as
+     * <table border="0" style="order-collapse: separate;border-spacing: 50px 0;" summary="">
+     * <tr align="left"><td>{@code *}</td><td>matches any number of any
+     * characters including none</td>
+     * <tr align="left"><td>{@code ?}</td><td>matches any single character</td>
+     * <tr align="left"><td>{@code [abc]}</td><td>matches one character given in
+     * the bracket</td>
+     * <tr align="left"><td>{@code [a-z]}</td><td>matches one character from the
+     * range given in the bracket</td>
+     * <tr align="left"><td>{@code [!abc]}</td><td>matches one character
+     * <i>not</i> given in the bracket</td>
+     * <tr align="left"><td>{@code [!a-z]}</td><td>matches one character
+     * <i>not</i> from the range given in the bracket</td>
+     * </table>
+     *
+     * <p>
+     * A small cache is used so that if a glob pattern has already been
+     * translated previously, the result from the cache will be returned.
+     *
+     * @param glob
+     * @return
+     */
+    public static Pattern createRegexPatternFromGlob(String glob) {
+
+        // First try the cache
+        Pattern pattern = PRECOMPILED_GLOB_CACHE.get(glob);
+        if (pattern != null) {
+            return pattern;
+        }
+
+        StringBuilder out = new StringBuilder();
+        out.append("^");
+        for (int i = 0; i < glob.length(); ++i) {
+            final char c = glob.charAt(i);
+            switch (c) {
+                case '*':
+                    out.append(".*?");
+                    break;
+                case '?':
+                    out.append(".{1}");
+                    break;
+                case '.':
+                    out.append("\\.");
+                    break;
+                case '\\':
+                    out.append("\\\\");
+                    break;
+                case '!':
+                    if (i > 0 && glob.charAt(i - 1) == '[') {
+                        out.append('^');
+                    } else {
+                        out.append(c);
+                    }
+                    break;
+                default:
+                    out.append(c);
+            }
+        }
+        out.append("$");
+        pattern = Pattern.compile(out.toString());
+        PRECOMPILED_GLOB_CACHE.put(glob, pattern);
+        return pattern;
+    }
+
+
+    
+    /**
+     * Converts list into semi-colon separated string where each element 
+     * is represented by the result of the {@code fn} function.
+     * 
+     * @param <T>
+     * @param list list of objects
+     * @param fn function which returns string
+     * @return string with elements separated by semi-colon
+     */
+    public static <T> String toSemiColonList(List<T> list, Function<T,String> fn) {
+        return list.stream()
+                .map(i -> fn.apply(i))
+                .collect(Collectors.joining(";"));
+    }
+    
+    /**
+     * Converts list into semi-colon separated string where each element 
+     * is represented by the result of {@link Object#toString()}.
+     * 
+     * @see #toSemiColonList(java.util.List, java.util.function.Function) 
+     * @param <T>
+     * @param list list of objects
+     * @return string with elements separated by semi-colon
+     */
+    public static <T> String toSemiColonList(List<T> list) {
+        return toSemiColonList(list, Object::toString);
+    }
+    
+    /**
+     * Converts a list of {@code InetAddress} into a semi-colon
+     * separated string. Each address is represented by the result
+     * of {@link InetAddress#getHostAddress()}.
+     * 
+     * @see #toSemiColonList(java.util.List, java.util.function.Function) 
+     * @param addresses
+     * @return semi-colon separated string of addresses in literal form
+     */
+    public static String toSemiColonListInetAddress(InetAddress[] addresses) {
+        return toSemiColonList(Arrays.asList(addresses), InetAddress::getHostAddress);
+    }
+
+    /**
+     * Converts an array of {@code InetAddress} into a semi-colon
+     * separated string. Each address is represented by the result
+     * of {@link InetAddress#getHostAddress()}.
+     * 
+     * @see #toSemiColonList(java.util.List, java.util.function.Function) 
+     * @param addresses
+     * @return semi-colon separated string of addresses in literal form
+     */
+    public static String toSemiColonListInetAddress(List<InetAddress> addresses) {
+        return toSemiColonList(addresses, InetAddress::getHostAddress);
+    }
+
+    
+    /**
+     * Checks if an IP address matches a given CIDR pattern.
+     * 
+     * The pattern must use the format:
+     * <pre>
+     *     ipLiteral/bitField
+     * </pre>
+     * 
+     * Examples of valid patterns:
+     * <pre>
+     *   198.95.249.79/32
+     *   198.95.0.0/16
+     *   3ffe:8311:ffff/48
+     * </pre>
+     * 
+     * <p>
+     * For IPv6 the {@code ipLiteral} is allowed to be incomplete at the end. If
+     * not complete, then a suffix of "::" is appended to the literal, e.g. the
+     * method will translate {@code "3ffe:8311:ffff/48"} to
+     * {@code "3ffe:8311:ffff::/48"}.
+     *
+     * <p>
+     * A number of validation checks are carried out on the {@code ipPrefix}
+     * argument and {@code false} will be returned if these checks fails:
+     * <ul>
+     *    <li>If {@code ipAddress} is IPv4 then {@code bitField}
+     *        must be between 8 and 32. 
+     *    </li>
+     *    <li>If {@code ipAddress} is IPv6 then {@code bitField} 
+     *        must be between 8 and 128. 
+     *    </li>
+     *    <li>The {@code ipLiteral} value must be a valid IPv4 literal or IPv6 literal.
+     *    </li>
+     *    <li>The IP protocol type of {@code ipLiteral} value must match the IP protocol
+     *        type of {@code ipAddress}.
+     *    </li>
+     * </ul>
+     * 
+     * <p>
+     * Note: The method was developed for the purpose of supporting the 
+     * {@link PacHelperMethodsMicrosoft#isInNetEx(java.lang.String, java.lang.String) 
+     * Microsoft isInNetEx()} extension to PAC scripting, but the method may
+     * have an appeal broader than this particular use case.
+     * 
+     * <br><br>
+     * 
+     * @param ipAddress address
+     * @param ipPrefix pattern
+     * @return true if the address, {@code ipAddress}, match the pattern, {@code ipPrefix}.
+     */
+    public static boolean ipPrefixMatch(InetAddress ipAddress, String ipPrefix) {
+        if (ipPrefix.indexOf('/') == -1) {
+            return false;
+        }
+        String[] parts = ipPrefix.trim().split("\\/");
+
+        int bitField = 0;
+        try {
+            bitField = Integer.parseInt(parts[1].trim());
+            if (!(bitField >= 0 && bitField <= 128)) {
+                return false;
+            }
+            if (ipAddress instanceof Inet4Address) {
+                if (bitField > 32) {
+                    return false;
+                }
+            }
+        } catch (NumberFormatException ex) {
+            return false;
+        }
+        String ipPatternStr = parts[0].trim();
+
+        InetAddress ipPattern = null;
+        if (ipAddress instanceof Inet4Address) {
+            if (IpAddressUtils.looksLikeIpv4Literal(ipPatternStr)) {
+                try {
+                    ipPattern = InetAddress.getByName(ipPatternStr);
+                } catch (UnknownHostException ex) {
+                    return false;
+                }
+            }
+        }
+        if (ipAddress instanceof Inet6Address) {
+            String ipPatternStrC = correctIPv6Str(ipPatternStr);
+            if (IpAddressUtils.looksLikeIpv6Literal(ipPatternStrC)) {
+                try {
+                    ipPattern = InetAddress.getByName(ipPatternStrC);
+                } catch (UnknownHostException ex) {
+                    return false;
+                }
+            }
+        }
+        if (ipPattern == null) {
+            return false;
+        }
+        if (!ipAddress.getClass().equals(ipPattern.getClass())) {
+            return false;
+        }
+        BigInteger mask = BigInteger.valueOf(-1).shiftLeft((ipPattern.getAddress().length * 8) - bitField); // mask = -1<<32-bits  or  mask = -1<<128-bits
+        BigInteger subnetMask = new BigInteger(ipPattern.getAddress()).and(mask);
+
+        return (new BigInteger(ipAddress.getAddress()))
+                .and(mask).equals(subnetMask);
+    }
+    
+    /**
+     * Completes an IPv6 literal address if it is incomplete at the end. 
+     * This is done by appending "::" if needed.
+     * @param s
+     * @return 
+     */
+    private static String correctIPv6Str(String s) {
+        int counter = 0;
+        for (int i = 0; i < s.length(); i++) {
+            if (s.charAt(i) == ':') {
+                counter++;
+                if (i > 0) {
+                    if (s.charAt(i - 1) == ':') {
+                        return s;
+                    }
+                }
+            }
+        }
+        if (counter != 7) {
+            return s + "::";
+        } else {
+            return s;
+        }
+    }
+    
+    /**
+     * Cleans a URI into a format suitable for passing to the PAC script.
+     * (meaning suitable for passing as {@code url} argument to 
+     * {@code FindProxyForURL(url, host)} or {@code FindProxyForURLEx(url, host)} 
+     * functions).
+     * <p>
+     * Because a PAC script is downloaded from a potentially malicious source it
+     * may contain harmful code. Therefore, the amount of information passed to
+     * the script should be limited to what is strictly necessary for the script
+     * to make decisions about choice of proxy. Anything in the URL which can
+     * potentially identity the user or which may contain session specific
+     * information should be removed before passing to script.
+     * 
+     * <p>
+     * The following is removed:
+     * <ul>
+     *   <li><i>{@code user-info}</i></li>
+     *   <li><i>{@code path}</i> and everything that follows after</li>
+     * </ul>
+     * 
+     * <p>
+     * Example:
+     * <pre>
+     *    https://mary@netbeans.apache.org:8081/path/to/something?x1=Christmas&amp;user=unknown
+     * becomes
+     *    https://netbeans.apache.org:8081/
+     * </pre>
+     * 
+     * <p>
+     * Note that the majority of PAC scripts out there do not make use of the
+     * {@code url} parameter at all. Instead they only use the {@code host}
+     * parameter. The stripping of information means that the {@code url}
+     * parameter only has two pieces of information that the {@code host} 
+     * parameter doesn't have: protocol and port number.
+     * <br>
+     *
+     * @param uri URL to be cleansed
+     * @return stripped URL string
+     */
+    public static String toStrippedURLStr(URI uri) {
+        
+        String portStr = (uri.getPort() == -1) ? "" : ":" + uri.getPort();
+        return
+                uri.getScheme()
+                + "://"
+                + uri.getHost()
+                + portStr
+                + "/";  // Chrome seems to always append the slash so we do it too
+    }
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/PacValidationException.java b/core.network/src/org/netbeans/core/network/proxy/pac/PacValidationException.java
new file mode 100644
index 0000000..b96d17d
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/PacValidationException.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+/**
+ * Thrown whenever there are errors in the evaluation of the result
+ * returned from the {@code FindProxyForURL(url, host)} function
+ * in the PAC JavaScript.
+ * 
+ * <p>
+ * In NetScape's original paper for the PAC script they define that 
+ * the result of the function must be a string, made up of the following 
+ * building blocks, separated by a semicolon:
+ * <pre>
+ *   DIRECT|PROXY &lt;host&gt;:&lt;port&gt;|SOCKS &lt;host&gt;:&lt;port&gt;
+ * </pre>
+ * 
+ * For example, a legal return value might be:
+ * <pre>
+ *   PROXY anaconda:8099; PROXY 192.168.1.42:8100; DIRECT
+ * </pre>
+ * 
+ * 
+ * @author lbruun
+ */
+public class PacValidationException extends Exception {
+
+    public PacValidationException(String value, String msg) {
+        super("Cannot interpret value \"" + value  + "\" : " + msg);
+    }
+    
+    public PacValidationException(String msg) {
+        super(msg);
+    }
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/datetime/DateRange.java b/core.network/src/org/netbeans/core/network/proxy/pac/datetime/DateRange.java
new file mode 100644
index 0000000..782e1aa
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/datetime/DateRange.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac.datetime;
+
+import java.util.Calendar;
+
+/**
+ * Evaluates a date against a date range.
+ *
+ * - Years are 4-digit.
+ * - Months are zero-based (January = 0).
+ * - day of month is 1-31. The range for this value is deliberately not
+ *    validated against date, meaning 31st of February is valid. This
+ *    creates a lenient evaluation.
+ * 
+ * Handles correctly the situation where a date range passes into next period.
+ * Examples:
+ * 
+ *   - Only month range is defined. monthMin=11 monthMax=02.  The period from 
+ *     December 1st to end of February.
+ *   - Only day of month range is defined. dateMin=27 dateMax=5. The days from the 
+ *     27th of each month to the 5th of the next month.
+ * 
+ * @author lbruun
+ */
+class DateRange {
+
+    private final static int UNDEFINED = -1;
+    private final int yearMin;
+    private final int yearMax;
+    private final int monthMin;
+    private final int monthMax;
+    private final int dateMin;
+    private final int dateMax;
+
+    public DateRange(int yearMin, int yearMax, int monthMin, int monthMax, int dateMin, int dateMax) {
+        this.yearMin = yearMin;
+        this.yearMax = ((yearMax == UNDEFINED) && (yearMin != UNDEFINED)) ? yearMin : yearMax;
+        this.monthMin = monthMin;
+        this.monthMax = ((monthMax == UNDEFINED) && (monthMin != UNDEFINED)) ? monthMin : monthMax;
+        this.dateMin = dateMin;
+        this.dateMax = ((dateMax == UNDEFINED) && (dateMin != UNDEFINED)) ? dateMin : dateMax;
+    }
+
+    public boolean isInRange(Calendar cal) {
+
+        int year = cal.get(Calendar.YEAR);
+        int month = cal.get(Calendar.MONTH);
+        int date = cal.get(Calendar.DATE);
+        if (yearDefined()) {
+            if (!(year >= yearMin && year <= yearMax)) {
+                return false;
+            }
+        }
+        if (monthDefined()) {
+            if (yearDefined()) {
+                if (year == yearMin) {
+                    if (!(month >= monthMin)) {
+                        return false;
+                    }
+                }
+                if (year == yearMax) {
+                    if (!(month <= monthMax)) {
+                        return false;
+                    }
+                }
+            } else {
+                if (monthMin <= monthMax) { // month range is fully within a year
+                    if (!(month >= monthMin && month<= monthMax)) {
+                        return false;
+                    }
+                } else {   // month range is rolling over into next year
+                    if (!(month >= monthMin || month<= monthMax)) {
+                        return false;
+                    }
+                }
+            }
+        } 
+        if (dateDefined()) {
+            boolean checkDateRange = false;
+            if (monthDefined()) {
+                if (yearDefined()) {
+                    if ((year == yearMin && month == monthMin) || (year == yearMax && month == monthMax)) {
+                        checkDateRange = true;
+                    }
+                } else {
+                    if (month == monthMin || month == monthMax) {
+                        checkDateRange = true;
+                    }
+                }
+            } else {
+                checkDateRange = true;
+            }
+            if (checkDateRange) {
+                if (dateMin <= dateMax) {  // date range is fully within a month
+                    if (!(date >= dateMin && date <= dateMax)) {
+                        return false;
+                    }
+                } else {  // date range is rolling over into next month
+                    if (!(date >= dateMin || date <= dateMax)) {
+                        return false;
+                    }
+                }
+            }
+        }
+        return true;
+    }
+    
+    private boolean yearDefined() {
+        return (!(yearMin == UNDEFINED && yearMax == UNDEFINED));
+    }
+    private boolean monthDefined() {
+        return (!(monthMin == UNDEFINED && monthMax == UNDEFINED));
+    }
+    private boolean dateDefined() {
+        return (!(dateMin == UNDEFINED && dateMax == UNDEFINED));
+    }
+    
+    
+    public static DateRangeBuilder getBuilder() {
+        return new DateRangeBuilder();
+    }
+    
+    public static class DateRangeBuilder {
+        private int yearMin = UNDEFINED;
+        private int yearMax = UNDEFINED;
+        private int monthMin = UNDEFINED;
+        private int monthMax = UNDEFINED;
+        private int dateMin = UNDEFINED;
+        private int dateMax = UNDEFINED;
+
+        private DateRangeBuilder() {
+        }
+
+        public DateRangeBuilder setYear(int yearMin, int yearMax) {
+            this.yearMin = yearMin;
+            this.yearMax = yearMax;
+            return this;
+        }
+
+        public DateRangeBuilder setMonth(int monthMin, int monthMax) {
+            this.monthMin = monthMin;
+            this.monthMax = monthMax;
+            return this;
+        }
+
+        /**
+         * Sets the day of month restrictions. These values are generally
+         * between 1 and 31 but are not enforced by this class.
+         */
+        public DateRangeBuilder setDate(int dateMin, int dateMax) {
+            this.dateMin = dateMin;
+            this.dateMax = dateMax;
+            return this;
+        }
+
+        public DateRange createDateRange() {
+            return new DateRange(yearMin, yearMax, monthMin, monthMax, dateMin, dateMax);
+        }
+
+    }
+
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/datetime/PacUtilsDateTime.java b/core.network/src/org/netbeans/core/network/proxy/pac/datetime/PacUtilsDateTime.java
new file mode 100644
index 0000000..e08d2ce
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/datetime/PacUtilsDateTime.java
@@ -0,0 +1,374 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac.datetime;
+
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+import org.netbeans.core.network.proxy.pac.PacHelperMethodsNetscape;
+
+/**
+ * Methods and constants useful in PAC script evaluation, specifically
+ * date/time related.
+ * 
+ * @author lbruun
+ */
+public class PacUtilsDateTime {
+
+    /**
+     * List of valid weekday names as used in the Netscape specification.
+     * <p>
+     * Content: {@code  SUN  MON  TUE  WED  THU  FRI  SAT}
+     *
+     */
+    public final static List<String> WEEKDAY_NAMES = Collections.unmodifiableList(
+            Arrays.asList("SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"));
+
+    /**
+     * List of valid month names as used in the Netscape specification.
+     * <p>
+     * Content: {@code JAN  FEB  MAR  APR  MAY  JUN  JUL  AUG  SEP  OCT  NOV  DEC}
+     *
+     */
+    public final static List<String> MONTH_NAMES = Collections.unmodifiableList(
+            Arrays.asList("JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"));
+
+    private static final TimeZone UTC_TIME = TimeZone.getTimeZone("UTC");
+
+    
+    /**
+     * Evaluates if now is within a weekday range. Method arguments are as described
+     * for {@link PacHelperMethodsNetscape#weekdayRange(Object...) }
+     * 
+     * @param now
+     * @param args
+     * @return true if within range
+     * @throws PacUtilsDateTime.PacDateTimeInputException if arguments were invalid
+     */
+    public static boolean isInWeekdayRange(Date now, Object... args) throws PacDateTimeInputException {
+        int params = getNoOfParams(args);
+        boolean gmt = usesGMT(args);
+        Calendar cal = getCalendar(now, gmt);
+        if (gmt) {
+            params--;
+        }
+        if (!(params >=1 && params <=2)) {
+            throw new PacDateTimeInputException("invalid number of arguments");
+        }
+        final int wdNumMin;
+        final int wdNumMax;
+        if (params == 1) {
+            wdNumMin = getWeekday(args[0].toString());
+            wdNumMax = wdNumMin;
+        } else {
+            wdNumMin = getWeekday(args[0].toString());
+            wdNumMax = getWeekday(args[1].toString());
+        }
+        
+        int wdNum = cal.get(Calendar.DAY_OF_WEEK);
+                
+        if (wdNumMin <= wdNumMax) {
+            return ( wdNum >= wdNumMin && wdNum <= wdNumMax);
+        } else {
+            return ( wdNum >= wdNumMin || wdNum <= wdNumMax);
+        }        
+    }
+    
+    
+    /**
+     * Evaluates if now is within a time range. Method arguments are as described
+     * for {@link PacHelperMethodsNetscape#timeRange(Object...) }
+     * 
+     * @param now
+     * @param args
+     * @return true if within range
+     * @throws PacUtilsDateTime.PacDateTimeInputException if arguments were invalid
+     */
+    public static boolean isInTimeRange(Date now, Object... args) throws PacDateTimeInputException {
+        int params = getNoOfParams(args);
+        boolean gmt = usesGMT(args);
+        Calendar cal = getCalendar(now, gmt);
+        if (gmt) {
+            params--;
+        }
+        if (!(params >= 1 && params <= 6) || params == 5 || params == 3) {
+            throw new PacDateTimeInputException("invalid number of arguments");
+        }
+
+        TimeRange.TimeRangeBuilder trBld = TimeRange.getBuilder();
+
+        if (params == 1) {
+            trBld   .setHourMinMax(getHour(args[0]), getHour(args[0]));
+        }
+        if (params == 2) {
+            trBld   .setHourMinMax(getHour(args[0]),getHour(args[1]));
+            if (getHour(args[0]) != getHour(args[1])) {
+                trBld.setMinuteMinMax(0, 0);
+            }
+        }
+        if (params == 4) {
+            trBld   .setHourMinMax(getHour(args[0]), getHour(args[2]))
+                    .setMinuteMinMax(getMinute(args[1]),getMinute(args[3]))
+                    .setSecondMinMax(0, 0);
+        }
+        if (params == 6) {
+            trBld   .setHourMinMax(getHour(args[0]),getHour(args[3]))
+                    .setMinuteMinMax(getMinute(args[1]),getMinute(args[4]))
+                    .setSecondMinMax(getSecond(args[2]),getSecond(args[5]));
+        }
+        TimeRange timeRange = trBld.createTimeRange();
+        
+        return timeRange.isInRange(cal);
+    }
+
+
+    /**
+     * Evaluates if now is within a date range. Method arguments are as described
+     * for {@link PacHelperMethodsNetscape#dateRange(Object...)  }
+     * 
+     * @param now 
+     * @param args arguments
+     * @return true if within range
+     * @throws PacUtilsDateTime.PacDateTimeInputException if arguments were invalid
+     */
+    public static boolean isInDateRange(Date now, Object... args) throws PacDateTimeInputException {
+        int params = getNoOfParams(args);
+        boolean gmt = usesGMT(args);
+        Calendar cal = getCalendar(now, gmt);
+        if (gmt) {
+            params--;
+        }
+        if (!(params >= 1 && params <= 6) || params == 5 || params == 3) {
+            throw new PacDateTimeInputException("invalid number of arguments");
+        }
+    
+        DateRange.DateRangeBuilder drBld = DateRange.getBuilder();
+        if (params == 1) {
+            if (isYear(args[0])) {
+                int y = getYear(args[0]);
+                drBld.setYear(y, y);
+            } else if (isMonth(args[0])) {
+                int m = getMonth(args[0].toString());
+                drBld.setMonth(m, m);
+            } else if (isDate(args[0])) {
+                int d = getDate(args[0]);
+                drBld.setDate(d, d);
+            } else {
+                throw new PacDateTimeInputException("invalid argument : " + args[0].toString());
+            }
+        }
+        if (params == 2) {
+            if (isYear(args[0])) {
+                drBld.setYear(getYear(args[0]), getYear(args[1]));
+            } else if (isMonth(args[0])) {
+                drBld.setMonth(getMonth(args[0].toString()), getMonth(args[1].toString()));
+            } else if (isDate(args[0])) {
+                drBld.setDate(getDate(args[0]), getDate(args[1]));
+            } else {
+                throw new PacDateTimeInputException("invalid argument : " + args[0].toString());
+            }
+        }
+        if (params == 4) {
+            if (isMonth(args[0])) {
+                drBld   .setYear(getYear(args[1]), getYear(args[3]))
+                        .setMonth(getMonth(args[0].toString()), getMonth(args[2].toString()));
+            } else if (isDate(args[0])) {
+                drBld   .setMonth(getMonth(args[1].toString()), getMonth(args[3].toString()))
+                        .setDate(getDate(args[0]), getDate(args[2]));
+            } else {
+               throw new PacDateTimeInputException("invalid argument : " + args[0].toString()); 
+            }
+        }
+        if (params == 6) {
+            drBld   .setYear(getYear(args[2]), getYear(args[5]))
+                    .setMonth(getMonth(args[1].toString()), getMonth(args[4].toString()))
+                    .setDate(getDate(args[0]), getDate(args[3]));
+        }
+        
+        DateRange dateRange = drBld.createDateRange();
+        
+        return dateRange.isInRange(cal);
+    }
+
+
+
+    private static boolean isMonth(Object obj) {
+        return (obj instanceof CharSequence);
+    }
+    
+    private static boolean isYear(Object obj) {
+        try {
+            int val = getInteger(obj);
+            return (val >= 1000);
+        } catch (PacDateTimeInputException ex) {
+            return false;
+        }
+    }
+    
+    private static boolean isDate(Object obj) {
+        try {
+            int val = getInteger(obj);
+            return (val >= 1 && val <= 31);
+        } catch (PacDateTimeInputException ex) {
+            return false;
+        }
+    }
+
+    private static int getDate(Object obj) throws PacDateTimeInputException {
+        if  (!isDate(obj)) {
+            throw new PacDateTimeInputException("value " + obj.toString() + " is not a valid day of month");
+        }
+        return getInteger(obj);
+    }
+    
+    private static int getYear(Object obj) throws PacDateTimeInputException {
+        if  (!isYear(obj)) {
+            throw new PacDateTimeInputException("value " + obj.toString() + " is not a valid year");
+        }
+        return getInteger(obj);
+    }
+    
+    private static int getWeekday(String wd) throws PacDateTimeInputException { 
+        int indexOf = WEEKDAY_NAMES.indexOf(wd);
+        if (indexOf == -1) {
+            throw new PacDateTimeInputException("Unknown weekday name : \"" + wd + "\"");
+        }
+        return indexOf+1;  // In Calendar, the first weekday (Sunday) is 1
+    }
+
+    private static int getMonth(String month) throws PacDateTimeInputException { 
+        int indexOf = MONTH_NAMES.indexOf(month);
+        if (indexOf == -1) {
+            throw new PacDateTimeInputException("Unknown month name : \"" + month + "\"");
+        }
+        return indexOf;  // In Calendar, January is 0
+    }
+    
+    
+    private static int getInteger(Object obj) throws PacDateTimeInputException {
+        if (obj instanceof Integer || obj instanceof Long) {
+            return ((Number) obj).intValue();
+        }
+        if (obj instanceof String) {
+            try {
+                return Integer.parseInt((String) obj);
+            } catch (NumberFormatException ex) {
+            }
+        }
+        throw new PacDateTimeInputException("value is " + obj + " is not an integer");
+    }
+    
+    private static int getHour(Object obj) throws PacDateTimeInputException {
+        int hour = getInteger(obj);
+        if (!(hour >= 0 && hour <= 23)) {
+            throw new PacDateTimeInputException("value is " + hour + " is not a valid hour of day (0-23)");
+        }
+        return hour;
+    }
+    
+    private static int getMinute(Object obj) throws PacDateTimeInputException {
+        int min = getInteger(obj);
+        if (!(min >= 0 && min <= 59)) {
+            throw new PacDateTimeInputException("value is " + min + " is not a valid minute (0-59)");
+        }
+        return min;
+    }
+    
+    private static int getSecond(Object obj) throws PacDateTimeInputException {
+        int sec = getInteger(obj);
+        if (!(sec >= 0 && sec <= 59)) {
+            throw new PacDateTimeInputException("value is " + sec + " is not a valid second (0-59)");
+        }
+        return sec;
+    }
+    
+    private static Calendar getCalendar(Date now, boolean useGMT) {        
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(now);
+        if (useGMT) {
+            cal.setTimeZone(UTC_TIME);
+        }
+        return cal;
+    }
+    
+    
+    /**
+     * Gets the number of true arguments passed to a JavaScript
+     * function. This is done by counting the number of arguments of type 
+     * {@code Number} or {@code CharSequence}. 
+     * 
+     * <p>
+     * This is a convenience method useful when implementing
+     * {@link PacHelperMethodsNetscape#dateRange(java.lang.Object...) dateRange()}
+     * ,
+     * {@link PacHelperMethodsNetscape#timeRange(java.lang.Object...) timeRange()}
+     * or
+     * {@link PacHelperMethodsNetscape#weekdayRange(java.lang.Object...) weekdayRange()}
+     * 
+     * <p>
+     * Note: In Nashorn, JavaScript function arguments that are not used in the
+     * call will have a type of {@code Undefined}.
+     *
+     * @param objs
+     * @return 
+     */
+    public static int getNoOfParams(Object... objs) {
+        int params=0;
+        for(Object obj : objs) {
+            if (obj == null) {  // don't really know if parameters 
+                                // will ever be null when the Java method is 
+                                // is invoked from JavaScript. I believe not, i.e
+                                // they will be of class Undefined rather than null.
+                continue;
+            }
+            // Only parameters of type CharSequence (String) and 
+            // Number (Integer, Long, etc) are relevant.
+            if ((obj instanceof Number) || (obj instanceof CharSequence)) {
+                params++;
+            }
+        }
+        return params;
+    }
+    
+    /**
+     * Evaluates if the last parameter of a parameter array is "GMT".
+     * @param args parameters
+     * @return 
+     */
+    public static boolean usesGMT(Object... args) {
+        int params = getNoOfParams(args);
+        if (args[params - 1] instanceof CharSequence) {
+            String p = args[params - 1].toString();
+            if (p.equals("GMT")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * Validation errors on input to {@code weekdayRange()}, 
+     * {@code timeRange()} and {@code dateRange()}.
+     */
+    public static class PacDateTimeInputException extends Exception  {
+        public PacDateTimeInputException(String msg) {
+            super(msg);
+        }
+    }
+    
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/datetime/TimeRange.java b/core.network/src/org/netbeans/core/network/proxy/pac/datetime/TimeRange.java
new file mode 100644
index 0000000..c603ad0
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/datetime/TimeRange.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac.datetime;
+
+import java.util.Calendar;
+
+/**
+ * Evaluates a time of day against a time range.
+ * 
+ * - hour of day is 24 h based.
+ * - minute is 0-59.
+ * - second is 0-59.
+ * 
+ * Handles correctly the situation where a time range passes into next period.
+ * Examples:
+ * 
+ *   - Only minute range is defined. minuteMin=55 minuteMax=05.  This means
+ *     a 10 minute interval around every hour change from 5 minutes before
+ *     the hour, to 5 minutes after the hour.
+ *   - Only second range is defined. secondMin=55 secondMax=05.  This means
+ *     a 10 second interval around every minute change, from 5 seconds before
+ *     the minute change, to 5 seconds after the minute change.
+ *   - Interval 23:35:00 to 00:25:00. This means a 55 minute interval around
+ *     every day change.
+ * 
+ * @author lbruun
+ */
+class TimeRange {
+
+    private final static int UNDEFINED = -1;
+    private final int hourMin;
+    private final int hourMax;
+    private final int minuteMin;
+    private final int minuteMax;
+    private final int secondMin;
+    private final int secondMax;
+
+    public TimeRange(int hourMin, int hourMax, int minuteMin, int minuteMax, int secondMin, int secondMax) {
+        this.hourMin = hourMin;
+        this.hourMax = ((hourMax == UNDEFINED) && (hourMin != UNDEFINED)) ? hourMin : hourMax;
+        this.minuteMin = minuteMin;
+        this.minuteMax = ((minuteMax == UNDEFINED) && (minuteMin != UNDEFINED)) ? minuteMin : minuteMax;
+        this.secondMin = secondMin;
+        this.secondMax = ((secondMax == UNDEFINED) && (secondMin != UNDEFINED)) ? secondMin : secondMax;
+    }
+
+    public boolean isInRange(Calendar cal) {
+
+        int hour = cal.get(Calendar.HOUR_OF_DAY);
+        int minute = cal.get(Calendar.MINUTE);
+        int second = cal.get(Calendar.SECOND);
+        if (hourDefined()) {
+            if (hourMin <= hourMax) {
+                if (!(hour >= hourMin && hour <= hourMax)) {
+                    return false;
+                }
+            } else {
+                if (!(hour >= hourMin || hour <= hourMax)) {
+                    return false;
+                }
+            }
+        }
+        if (minuteDefined()) {
+            if (hourDefined()) {
+                if (hour == hourMin) {
+                    if (!(minute >= minuteMin)) {
+                        return false;
+                    }
+                }
+                if (hour == hourMax) {
+                    if (!(minute <= minuteMax)) {
+                        return false;
+                    }
+                }
+            } else {
+                if (minuteMin <= minuteMax) { // minute range is fully within an hour
+                    if (!(minute >= minuteMin && minute <= minuteMax)) {
+                        return false;
+                    }
+                } else {   // minute range is passing into new hour
+                    if (!(minute >= minuteMin || minute <= minuteMax)) {
+                        return false;
+                    }
+                }
+            }
+        } 
+        if (secondDefined()) {
+            if (minuteDefined()) {
+                if (hourDefined()) {
+                    if (hour == hourMin && minute == minuteMin) {
+                        if (!(second >= secondMin)) {
+                            return false;
+                        }
+                    }
+                    if (hour == hourMax && minute == minuteMax) {
+                        if (!(second <= secondMax)) {
+                            return false;
+                        }
+                    }
+                } else {
+                    if (minute == minuteMin) {
+                        if (!(second >= secondMin)) {
+                            return false;
+                        }
+                    }
+                    if (minute == minuteMax) {
+                        if (!(second <= secondMax)) {
+                            return false;
+                        }
+                    }
+                }
+            } else  {  // only secondMin and secondMax are defined
+                if (secondMin <= secondMax) {  // second range is fully within a minute
+                    if (!(second >= secondMin && second <= secondMax)) {
+                        return false;
+                    }
+                } else {  // second range is rolling over into next minute
+                    if (!(second >= secondMin || second <= secondMax)) {
+                        return false;
+                    }
+                }
+            }
+        }
+        return true;
+    }
+    
+    private boolean hourDefined() {
+        return (!(hourMin == UNDEFINED && hourMax == UNDEFINED));
+    }
+    private boolean minuteDefined() {
+        return (!(minuteMin == UNDEFINED && minuteMax == UNDEFINED));
+    }
+    private boolean secondDefined() {
+        return (!(secondMin == UNDEFINED && secondMax == UNDEFINED));
+    }
+    
+    
+    public static TimeRangeBuilder getBuilder() {
+        return new TimeRangeBuilder();
+    }
+    
+    public static class TimeRangeBuilder {
+        private int hourMin = UNDEFINED;
+        private int hourMax = UNDEFINED;
+        private int minuteMin = UNDEFINED;
+        private int minuteMax = UNDEFINED;
+        private int secondMin = UNDEFINED;
+        private int secondMax = UNDEFINED;
+
+        private TimeRangeBuilder() {}
+
+        public TimeRangeBuilder setHourMinMax(int hourMin, int hourMax) {
+            this.hourMin = hourMin;
+            this.hourMax = hourMax;
+            return this;
+        }
+
+        public TimeRangeBuilder setMinuteMinMax(int minuteMin, int minuteMax) {
+            this.minuteMin = minuteMin;
+            this.minuteMax = minuteMax;
+            return this;
+        }
+
+        public TimeRangeBuilder setSecondMinMax(int secondMin, int secondMax) {
+            this.secondMin = secondMin;
+            this.secondMax = secondMax;
+            return this;
+        }
+
+        public TimeRange createTimeRange() {
+            return new TimeRange(hourMin, hourMax, minuteMin, minuteMax, secondMin, secondMax);
+        }
+    }
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/datetime/package-info.java b/core.network/src/org/netbeans/core/network/proxy/pac/datetime/package-info.java
new file mode 100644
index 0000000..a84e812
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/datetime/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * Proxy Auto-Config (PAC) script related, specifically around
+ * Java support for the JavaScript helper functions {@code dateRange()},
+ * {@code timeRange()} and {@code weekdayRange()}.
+ * 
+ */
+package org.netbeans.core.network.proxy.pac.datetime;
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/impl/ClassFilterPacHelpers.java b/core.network/src/org/netbeans/core/network/proxy/pac/impl/ClassFilterPacHelpers.java
new file mode 100644
index 0000000..04b2d4d
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/impl/ClassFilterPacHelpers.java
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.proxy.pac.impl;
+
+import jdk.nashorn.api.scripting.ClassFilter;
+import org.netbeans.core.network.proxy.pac.PacHelperMethods;
+
+/**
+ * Nashorn class filter which helps us create a sandboxed JavaScript execution
+ * environment which only has access to the Helper methods, nothing more.
+ * 
+ * <p>Note that the ClassFilter feature is specific to Nashorn (Rhino had the
+ * {@code ClassShutter} class for this purpose), but the feature did not appear 
+ * until Java 8u40.
+ * 
+ * @author lbruun
+ */
+class ClassFilterPacHelpers implements ClassFilter {
+
+    @Override
+    public boolean exposeToScripts(String string) {
+        // The only Java class the PAC script is allowed to
+        // make use of is the PAC Helpers, nothing more. 
+        return string.equals(PacHelperMethods.class.getName());
+    }
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/impl/HelperScriptFactory.java b/core.network/src/org/netbeans/core/network/proxy/pac/impl/HelperScriptFactory.java
new file mode 100644
index 0000000..4346a26
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/impl/HelperScriptFactory.java
@@ -0,0 +1,139 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.proxy.pac.impl;
+
+/**
+ * Auto-generates JavaScript source for declaration of
+ * helper functions.
+ * 
+ * @author lbruun
+ */
+class HelperScriptFactory {
+    
+    // Netscape functions 
+    private static final JsHelperFunction[] JS_HELPER_FUNCTIONS_NS = new JsHelperFunction[]{
+        new JsHelperFunction("isPlainHostName",     new String[]{"host"}, Boolean.class),
+        new JsHelperFunction("dnsDomainIs",         new String[]{"host", "domain"}, Boolean.class),
+        new JsHelperFunction("localHostOrDomainIs", new String[]{"host", "hostdom"}, Boolean.class),
+        new JsHelperFunction("isResolvable",        new String[]{"host"}, Boolean.class),
+        new JsHelperFunction("isInNet",             new String[]{"host", "pattern", "mask"}, Boolean.class),
+        new JsHelperFunction("dnsResolve",          new String[]{"host"}, String.class),
+        new JsHelperFunction("myIpAddress",         new String[]{}, String.class),
+        new JsHelperFunction("dnsDomainLevels",     new String[]{"host"}, Integer.class),
+        new JsHelperFunction("shExpMatch",          new String[]{"str", "shexp"}, Boolean.class),
+        new JsHelperFunction("weekdayRange",        new String[]{"wd1", "wd2", "gmt"}, Boolean.class),
+        new JsHelperFunction("dateRange",           new String[]{"day1", "month1", "year1", "day2", "month2", "year2", "gmt"}, Boolean.class),
+        new JsHelperFunction("timeRange",           new String[]{"hour1", "min1", "sec1", "hour2", "min2", "sec2", "gmt"}, Boolean.class),
+    };
+    
+    // Microsoft functions 
+    private static final JsHelperFunction[] JS_HELPER_FUNCTIONS_MS = new JsHelperFunction[]{
+        new JsHelperFunction("isResolvableEx",      new String[]{"host"}, Boolean.class),
+        new JsHelperFunction("isInNetEx",           new String[]{"host", "ipPrefix"}, Boolean.class),
+        new JsHelperFunction("dnsResolveEx",        new String[]{"host"}, String.class),
+        new JsHelperFunction("myIpAddressEx",       new String[]{}, String.class),
+        new JsHelperFunction("sortIpAddressList",   new String[]{"ipAddressList"}, String.class),
+        new JsHelperFunction("getClientVersion",    new String[]{}, String.class)
+    };
+    
+    // Debug functions (not part of any spec)
+    private static final JsHelperFunction[] JS_HELPER_FUNCTIONS_DEBUG = new JsHelperFunction[]{
+        new JsHelperFunction("alert",               new String[]{"txt"}, Void.class)
+    };
+
+    private HelperScriptFactory() {
+    }
+    
+    /**
+     * Gets JavaScript source with PAC Helper function declarations.
+     * 
+     * @param bridgeObjectName name of Java object which contains Java methods, 
+     *   named similarly to the JavaScript PAC helper functions and with 
+     *   similar arg list. This Java object acts as the bridge between the 
+     *   JavaScript world and the Java world and must be an instance 
+     *   of {@link org.netbeans.network.proxy.pac.PacHelperMethods PacHelperMethods}.
+     * 
+     * @return JavaScript source code
+     */
+    public static String getPacHelperSource(String bridgeObjectName) {
+        StringBuilder sb = new StringBuilder(2000);
+        addFunctionDecls(sb, JS_HELPER_FUNCTIONS_NS, bridgeObjectName);
+        addFunctionDecls(sb, JS_HELPER_FUNCTIONS_MS, bridgeObjectName);
+        addFunctionDecls(sb, JS_HELPER_FUNCTIONS_DEBUG, bridgeObjectName);
+        return sb.toString();
+    }
+    
+    
+    private static void addFunctionDecls(StringBuilder sb, JsHelperFunction[] jsHelperFunctions, String bridgeObjectName) {
+        for (JsHelperFunction f : jsHelperFunctions) {
+            sb.append("function ");
+            sb.append(f.functionName);
+            sb.append('(');
+            addArgList(sb, f.argList);
+            sb.append(") {");
+            sb.append('\n');
+            sb.append("    return ");
+            boolean encloseReturnValue = false;
+            if (Number.class.isAssignableFrom(f.getClass())) {
+                encloseReturnValue = true;
+                sb.append("Number(");
+            }
+            if (f.returnType == String.class) {
+                encloseReturnValue = true;
+                sb.append("String(");
+            }
+            sb.append(bridgeObjectName);
+            sb.append('.');
+            sb.append(f.functionName);
+            sb.append('(');
+            addArgList(sb, f.argList);
+            sb.append(')');
+            if (encloseReturnValue) {
+                sb.append(')');
+            }
+            sb.append(";\n");
+            sb.append("}\n\n");
+        }
+    }
+    
+    private static void addArgList(StringBuilder sb, String[] argList) {
+        if (argList != null && argList.length > 0) {
+            for (int i = 0; i < argList.length; i++) {
+                sb.append(argList[i]);
+                if (i < argList.length - 1) {
+                    sb.append(", ");
+                }
+            }
+        }
+    }
+    
+    private static class JsHelperFunction {
+        String functionName;
+        String[] argList;
+        Class returnType;
+
+        public JsHelperFunction(String functionName, String[] argList, Class returnType) {
+            this.functionName = functionName;
+            this.argList = argList;
+            this.returnType = returnType;
+        }
+        
+    }
+    
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacHelperMethods.java b/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacHelperMethods.java
new file mode 100644
index 0000000..874f2d4
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacHelperMethods.java
@@ -0,0 +1,343 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.proxy.pac.impl;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import org.netbeans.core.network.utils.IpAddressUtils;
+import org.netbeans.core.network.utils.IpAddressUtils.IpTypePreference;
+import org.netbeans.core.network.utils.LocalAddressUtils;
+import org.netbeans.core.network.proxy.pac.PacHelperMethods;
+import org.netbeans.core.network.proxy.pac.PacUtils;
+import org.netbeans.core.network.proxy.pac.datetime.PacUtilsDateTime;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * NetBeans default implementation of a the PAC 'helper functions'.
+ * 
+ * <p>
+ * This implementation aims to be as complete and compatible as possible. It 
+ * implements both the original specification from Netscape, plus the additions
+ * from Microsoft.
+ * 
+ * <ul>
+ *    <li>Complete. Implements all methods from original Netscape specification
+ *        as well as all IPv6 aware additions from Microsoft.</li>
+ *    <li>Compatible. Aims for maximum compatibility with all browser 
+ *         implementations and aims to never fail and never stall.</li>
+ * </ul>
+ * 
+ * @author lbruun
+ */
+@ServiceProvider(service=PacHelperMethods.class)
+public class NbPacHelperMethods extends PacHelperMethods {
+
+    private static final Logger LOGGER = Logger.getLogger(NbPacHelperMethods.class.getName());
+
+    /** 
+     * Timeout (milliseconds) used for name service lookups.
+     */    
+    public static final int DNS_TIMEOUT_MS = 4000;
+
+    
+    // *************************************************************
+    //  Official helper functions.
+    // 
+    //  These are pure-Java implementations of the JavaScript
+    //  helper functions defined in Netscape's original document.
+    // *************************************************************
+    
+    @Override
+    public boolean isPlainHostName(String host) {
+        return !host.contains(".");
+    }
+
+    @Override
+    public boolean dnsDomainIs(String host, String domain) {
+        int dotPos = host.indexOf(".");
+        if (dotPos != -1 && (dotPos < host.length()-1)) {
+            if (host.substring(dotPos).equals(domain)) {
+                return true;
+            }
+            if (host.substring(dotPos+1).equals(domain)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    
+    @Override
+    public boolean localHostOrDomainIs(String host, String hostdom) {
+        if (host.equals(hostdom)) {
+            return true;
+        }
+        return host.equals(getDomains(hostdom)[0]);
+    }
+
+    @Override
+    public boolean isResolvable(String host) {
+        try {
+            IpAddressUtils.nameResolve(host, DNS_TIMEOUT_MS, IpTypePreference.IPV4_ONLY);
+            return true;
+        } catch (InterruptedException | UnknownHostException | TimeoutException ex) {
+            return false;
+        }
+    }
+
+    @Override
+    public String dnsResolve(String host) {
+        try {
+            return IpAddressUtils.nameResolve(host, DNS_TIMEOUT_MS, IpTypePreference.IPV4_ONLY)
+                    .getHostAddress();
+
+        } catch (InterruptedException | UnknownHostException | TimeoutException ex) {
+            // Returning null is what Chrome and Firefox do in this situation
+            return null; 
+        }
+    }
+
+    @Override
+    public String myIpAddress() {
+        return LocalAddressUtils.getMostLikelyLocalInetAddress(IpTypePreference.IPV4_ONLY).getHostAddress();
+    }
+
+    
+    @Override
+    public boolean isInNet(String host, String pattern, String mask) {
+        try {            
+            String hostIP = IpAddressUtils.nameResolve(host, 4000, IpTypePreference.IPV4_ONLY).getHostAddress();
+            String[] hostIPElements = hostIP.split("\\.");
+            String[] patternElements = pattern.split("\\.");
+            String[] maskElements = mask.split("\\.");
+
+            for (int i = 0; i < hostIPElements.length; i++) {
+                if (i < maskElements.length && maskElements[i].trim().equals("255")) {
+                    if (!hostIPElements[i].trim().equals(patternElements[i].trim())) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        } catch (InterruptedException | UnknownHostException | TimeoutException ex) {
+            return false;
+        }
+    }
+
+    @Override
+    public int dnsDomainLevels(String host) {
+        if (host == null) {
+            return 0;
+        }
+        return getNoOfOccurrences(host, '.');
+    }
+
+    @Override
+    public boolean shExpMatch(String str, String shexp) {
+        Pattern pattern = PacUtils.createRegexPatternFromGlob(shexp);
+        return pattern.matcher(str).matches();
+    }
+    
+
+    @Override
+    public boolean weekdayRange(Object... args) {
+        try {
+            return PacUtilsDateTime.isInWeekdayRange(new Date(), args);
+        } catch (PacUtilsDateTime.PacDateTimeInputException ex) {
+            LOGGER.log(Level.WARNING, "PAC script error : arguments passed to weekdayRange() function {0} are faulty: {1}", new Object[]{Arrays.toString(args), ex.getMessage()});
+            return false;
+        }
+    }
+
+    @Override
+    public boolean dateRange(Object... args) {
+        try {
+            return PacUtilsDateTime.isInDateRange(new Date(), args);
+        } catch (PacUtilsDateTime.PacDateTimeInputException ex) {
+            LOGGER.log(Level.WARNING, "PAC script error : arguments passed to dateRange() function {0} are faulty: {1}", new Object[]{Arrays.toString(args), ex.getMessage()});
+            return false;
+        }
+    }
+
+    @Override
+    public boolean timeRange(Object... args) {
+        try {
+            return PacUtilsDateTime.isInTimeRange(new Date(), args);
+        } catch (PacUtilsDateTime.PacDateTimeInputException ex) {
+            LOGGER.log(Level.WARNING, "PAC script error : arguments passed to timeRange() function {0} are faulty: {1}", new Object[]{Arrays.toString(args), ex.getMessage()});
+            return false;
+        }
+    }
+    
+
+    // *************************************************************
+    //  Microsoft extensions
+    // 
+    // *************************************************************
+    
+    
+    @Override
+    public boolean isResolvableEx(String host) {
+        try {
+            IpAddressUtils.nameResolve(host, DNS_TIMEOUT_MS, IpTypePreference.ANY_JDK_PREF)
+                    .getHostAddress();
+            return true;
+        } catch (InterruptedException | UnknownHostException | TimeoutException ex) {
+            return false;
+        }
+    }
+
+    @Override
+    public String dnsResolveEx(String host) {
+        try {
+            return IpAddressUtils.nameResolve(host, DNS_TIMEOUT_MS, IpTypePreference.ANY_JDK_PREF)
+                    .getHostAddress();
+        } catch (InterruptedException | UnknownHostException | TimeoutException ex) {
+            return "";
+        }
+    }
+
+    @Override
+    public String myIpAddressEx() {
+        InetAddress[] addresses = LocalAddressUtils.getMostLikelyLocalInetAddresses(IpTypePreference.ANY_JDK_PREF);
+        return PacUtils.toSemiColonListInetAddress(addresses);
+    }
+
+    @Override
+    public String sortIpAddressList(String ipAddressList) {
+        if (ipAddressList == null) {
+            return "";
+        }
+        
+        // We convert to InetAddress (because we know how to sort
+        // those) but at the same time we have to preserve the way
+        // the original input was represented and return in the same
+        // format.
+        String[] strComps = ipAddressList.split(";");
+        List<InetAddress> addressesB = new ArrayList<>();
+        for(String s : strComps) {
+            try {
+                addressesB.add(InetAddress.getByName(s.trim()));
+            } catch (UnknownHostException ex) {
+                return "";
+            }
+        }
+        List<InetAddress> addressesS = new ArrayList<>(addressesB);
+        IpAddressUtils.sortIpAddresses(addressesS, false);
+        
+        List<String> addressesStr = new ArrayList<>(addressesS.size());
+        for (InetAddress addr : addressesS) {
+            addressesStr.add(strComps[addressesB.indexOf(addr)].trim());
+        }
+        
+        return PacUtils.toSemiColonList(addressesStr);
+    }
+
+    @Override
+    public String getClientVersion() {
+        return "1.0";
+    }
+
+    @Override
+    public boolean isInNetEx(String ipAddress, String ipPrefix) {
+        if (ipAddress == null) {
+            return false;
+        }
+        if (ipPrefix == null) {
+            return false;
+        }
+        
+        InetAddress iAddr;
+        try {
+            iAddr = IpAddressUtils.nameResolve(ipAddress, DNS_TIMEOUT_MS, IpTypePreference.ANY_JDK_PREF);
+        } catch (InterruptedException | UnknownHostException | TimeoutException ex) {
+            return false;
+        }
+        
+        String[] ipPrefixes = ipPrefix.split(";");
+        for(String ipP : ipPrefixes) {
+            if (PacUtils.ipPrefixMatch(iAddr, ipP)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    
+    
+    // *************************************************************
+    //  Utility functions.
+    // 
+    //  Other functions - not defined in PAC spec, but still 
+    //  exposed to the JavaScript engine.
+    // *************************************************************
+
+    @Override
+    public void alert(String message) {
+        LOGGER.log(Level.INFO, "PAC script says : {0}", message);
+    }
+
+    
+    // *************************************************************
+    //  
+    // Other methods.
+    // Not exposed to JavaScript engine.
+    //
+    // *************************************************************
+    
+    
+
+    private static String[] getDomains(String host) {
+        String[] doms = host.split("\\.");
+        List<String> domsList = new ArrayList<>(doms.length);
+        for (String dom : doms) {
+            if (dom != null && (!dom.isEmpty())) {
+                domsList.add(dom);
+            }
+        }
+        return domsList.toArray(new String[domsList.size()]);
+    }
+
+    
+    /**
+     * Count no of occurrences of ch in str.
+     * @param str
+     * @param ch
+     * @return 
+     */
+    private static int getNoOfOccurrences(String str, char ch) {
+        int counter = 0;
+        for (int i = 0; i < str.length(); i++) {
+            if (str.charAt(i) == ch) {
+                counter++;
+            }
+        }
+        return counter;
+    }
+
+    
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluator.java b/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluator.java
new file mode 100644
index 0000000..c8bcfdd
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluator.java
@@ -0,0 +1,531 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.proxy.pac.impl;
+
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.script.Bindings;
+import javax.script.Invocable;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
+import org.netbeans.core.network.utils.SimpleObjCache;
+import org.netbeans.core.network.proxy.pac.PacHelperMethods;
+import org.netbeans.core.network.proxy.pac.PacJsEntryFunction;
+import org.netbeans.core.network.proxy.pac.PacValidationException;
+import org.netbeans.core.network.proxy.pac.PacParsingException;
+import org.openide.util.Exceptions;
+import org.netbeans.core.network.proxy.pac.PacScriptEvaluator;
+import org.netbeans.core.network.proxy.pac.PacUtils;
+import org.openide.util.Lookup;
+
+/**
+ * NetBeans implementation of a PAC script evaluator. This implementation
+ * is the one returned by {@link NbPacScriptEvaluatorFactory}.
+ * 
+ * <h3>Features comparison</h3>
+ * There are differences between how browsers have implemented the PAC
+ * evaluation functionality. In the following the Apache NetBeans implementation
+ * (this class) is pitched against some of the major browsers.<br><br>
+ * 
+ * <table summary="" style="table-layout: fixed; width:100%;" border="1" cellpadding="10" cellspacing="0">
+ *   <tr><th class="tablersh">Behavior</th>
+ *       <th>Apache<br>NetBeans</th>
+ *       <th>Chrome<br>{@code 61.0.3163.100}</th>
+ *       <th>Firefox<br>{@code 56.0.1}</th>
+ *       <th>IE 11<br>{@code 11.608.15063.0}</th>
+ *       <th>Edge<br>{@code 40.15063.0.0}</th>
+ *   </tr>
+ *   <tr>
+ *       <td class="tablersh">Entry point functions supported:<br>{@code FindProxyForURL()} and {@code FindProxyForURLEx()}</td>
+ *       <td>Both are supported. If both exist in the same PAC script then {@code FindProxyForURLEx()} is used.</td>
+ *       <td>Only {@code FindProxyForURL()}</td>
+ *       <td>Only {@code FindProxyForURL()}</td>
+ *       <td>Both.<br>Only one of them may be present.</td>
+ *       <td>Both.<br>Only one of them may be present.</td>
+ *   </tr>
+ *   <tr>
+ *      <td class="tablersh">Security: Sandboxed execution of PAC script</td>
+ *      <td>Yes</td>
+ *      <td>???</td>
+ *      <td>???</td>
+ *      <td>Yes</td>
+ *      <td>Yes</td>
+ *   </tr>
+ *   <tr>
+ *       <td class="tablersh">Security: Stripped URL<br>(Value passed in {@code url} parameter)</td>
+ *       <td>Yes<sup>1</sup></td>
+ *       <td>Yes<sup>1</sup><br>(but strangely not for HTTP, only for HTTPS)</td>
+ *       <td>Yes<sup>2</sup></td>
+ *       <td>Yes</td>
+ *       <td>Yes</td>
+ *   </tr>
+ *   <tr>
+ *       <td class="tablersh">Extended return value support<br>Netscape spec only allowed:<ul><li>{@code DIRECT}</li><li>{@code PROXY host:port}</li><li>{@code SOCKS host:port}</li></ul></td>
+ *       <td>In addition:<sup>3</sup><ul><li>{@code HTTP host:port}</li><li>{@code HTTPS host:port}</li><li>{@code SOCKS4 host:port}</li><li>{@code SOCKS5 host:port}</li></ul></td>
+ *       <td>???</td>
+ *       <td>In addition:<ul><li>{@code HTTP host:port}</li><li>{@code HTTPS host:port}</li><li>{@code SOCKS4 host:port}</li><li>{@code SOCKS5 host:port}</li></ul></td>
+ *       <td>???</td>
+ *       <td>???</td>
+ *   </tr>
+ *   <tr>
+ *       <td class="tablersh">Uses result cache</td>
+ *       <td>Yes<br>(based on {@code url} value)</td>
+ *       <td>Yes</td>
+ *       <td>???</td>
+ *       <td>Yes<br>(based on {@code host} value)</td>
+ *       <td>???</td>
+ *   </tr>
+ *   <tr>
+ *       <td class="tablersh">Support for <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/gg308476(v=vs.85).aspx">getClientVersion()</a></td>
+ *       <td>Yes<br>(returns "1.0")</td>
+ *       <td>No</td>
+ *       <td>No</td>
+ *       <td>Yes<br>(returns "1.0")</td>
+ *       <td>Yes<br>(returns "1.0")</td>
+ *   </tr>
+ *   <tr>
+ *      <td class="tablersh">Use of result cache
+ *           when script uses time sensitive functions:<br>
+ *           {@code weekdayRange()}, {@code dateRange()} and {@code timeRange()}
+ *       </td>
+ *      <td>Result cache not used</td>
+ *      <td>???</td>
+ *      <td>???</td>
+ *      <td>???</td>
+ *      <td>???</td>
+ *   </tr>
+ *   <tr>
+ *      <td class="tablersh">Name lookup timeout<sup>4</sup></td>
+ *      <td>Explicit.<br>(see {@link NbPacHelperMethods#DNS_TIMEOUT_MS DNS_TIMEOUT_MS})</td>
+ *      <td>???</td>
+ *      <td>Yes</td>
+ *      <td>???</td>
+ *      <td>???</td>
+ *   </tr>
+ *   <tr>
+ *      <td class="tablersh">Support for Date/Time functions with range crossing a boundary<sup>5</sup></td>
+ *      <td>Yes</td>
+ *      <td>???</td>
+ *      <td>Yes<br>since v49</td>
+ *      <td>???</td>
+ *      <td>???</td>
+ *   </tr>
+ *   <tr>
+ *      <td class="tablersh">myIpAddress()<sup>6</sup></td>
+ *      <td>Pretty good. Uses JNI. (At least far better than text book Java approach which consistently yields incorrect result)</td>
+ *      <td>Uses UDP to try to find IP address. Seems to be reliable.</td>
+ *      <td>???</td>
+ *      <td>???</td>
+ *      <td>???</td>
+ *   </tr>
+ * </table>
+ * 1) The following is removed: <i>{@code user-info}</i> and everything after the 
+ *    host name, however the value passed to the script always ends with a '/' 
+ *    character.<br>
+ * 2) Same as (1), except that <i>{@code user-info}</i> is not removed.<br>
+ * 3) However, when converted to Java {@link java.net.Proxy} object, all return 
+ *    types must be mapped to Java Proxy types {@code DIRECT}, 
+ *    {@code HTTP} and {@code SOCKS}, which means some finer grained information
+ *    is lost. (but is irrelevant)<br>
+ * 4) An implementation not using an explicit timeout will be at the mercy of 
+ *    the underlying OS or runtime environment. For example, for 2 name servers, 
+ *    the default timeout in JRE is 30 seconds.<br>
+ * 5) If date/time functions ({@code weekdayRange()}, {@code dateRange()} and 
+ *    {@code timeRange()}) allow values that crosses a value boundary. 
+ *    For example {@code timeRange(22,3}) for the range from 10 pm to 3 am, or 
+ *    {@code dateRange("DEC","MAR"}) for the range from Dec 1st to March 31st.<br>
+ * 6) How good is the {@code myIpAddress()} (or {@code myIpAddressEx()}) function
+ *    at finding the host's correct IP address, in particular on a multi-homed
+ *    computer.
+ *
+ * <h3>Customization</h3>
+ * The implementation for 
+ * {@link org.netbeans.core.network.proxy.pac.PacHelperMethods PacHelperMethods} is
+ * found via the global lookup. If you are unhappy with the implementation
+ * of the Helper Functions you can replace with your own.
+ * 
+ * @author lbruun
+ */
+public class NbPacScriptEvaluator implements PacScriptEvaluator {
+
+    private static final Logger LOGGER = Logger.getLogger(NbPacScriptEvaluator.class.getName());
+
+
+    private static final String JS_HELPER_METHODS_INSTANCE_NAME = "jsPacHelpers";
+    
+    private final boolean canUseURLCaching;
+    private final PacScriptEngine scriptEngine;
+    private final SimpleObjCache<URI,List<Proxy>> resultCache;
+    
+    private static final String PAC_PROXY = "PROXY";
+    private static final String PAC_DIRECT = "DIRECT";
+    private static final String PAC_SOCKS = "SOCKS";
+    private static final String PAC_SOCKS4_FFEXT = "SOCKS4"; // Mozilla Firefox extension. Not part of original Netscape spec.
+    private static final String PAC_SOCKS5_FFEXT = "SOCKS5"; // Mozilla Firefox extension. Not part of original Netscape spec.
+    private static final String PAC_HTTP_FFEXT = "HTTP"; // Mozilla Firefox extension. Not part of original Netscape spec.
+    private static final String PAC_HTTPS_FFEXT = "HTTPS"; // Mozilla Firefox extension. Not part of original Netscape spec.
+    private final boolean nashornJava8u40Available;
+    private final String pacScriptSource;
+
+
+    public NbPacScriptEvaluator(String pacSourceCocde) throws PacParsingException {
+        this.pacScriptSource = pacSourceCocde;
+        nashornJava8u40Available = getNashornJava8u40Available();
+        scriptEngine = getScriptEngine(pacSourceCocde);
+        canUseURLCaching = !usesTimeDateFunctions(pacSourceCocde);
+        if (canUseURLCaching) {
+            resultCache = new SimpleObjCache<>(100);
+        } else {
+            resultCache = null;
+        }
+    }
+
+    @Override
+    public List<Proxy> findProxyForURL(URI uri) throws PacValidationException {
+
+        List<Proxy> jsResultAnalyzed;
+        
+        // First try the cache
+        if (resultCache != null) {
+            jsResultAnalyzed = resultCache.get(uri);
+            if (jsResultAnalyzed != null) {
+                return jsResultAnalyzed;
+            }
+        }
+        try {
+            Object jsResult = scriptEngine.findProxyForURL(PacUtils.toStrippedURLStr(uri), uri.getHost()); 
+            jsResultAnalyzed = analyzeResult(uri, jsResult);
+            if (canUseURLCaching && (resultCache != null)) {
+                resultCache.put(uri, jsResultAnalyzed);   // save the result in the cache
+            }
+            return jsResultAnalyzed;
+        } catch (NoSuchMethodException ex) {
+            // If this exception occur at this time it is really, really unexpected. 
+            // We already gave the function a test spin in the constructor.
+            Exceptions.printStackTrace(ex);
+            return Collections.singletonList(Proxy.NO_PROXY);
+        } catch (ScriptException ex) {
+            LOGGER.log(Level.WARNING, "Error when executing PAC script function " + scriptEngine.getJsMainFunction().getJsFunctionName() + " : ", ex);
+            return Collections.singletonList(Proxy.NO_PROXY);
+        } catch (Exception ex) {  // for runtime exceptions
+            if (ex.getCause() != null) {
+                if (ex.getCause() instanceof ClassNotFoundException) {
+                    // Is someone trying to break out of the sandbox ?
+                    LOGGER.log(Level.WARNING, "The downloaded PAC script is attempting to access Java class ''{0}'' which may be a sign of maliciousness. You should investigate this with your network administrator.", ex.getCause().getMessage());
+                    return Collections.singletonList(Proxy.NO_PROXY);
+                }
+            }
+            // other unforseen errors
+            LOGGER.log(Level.WARNING, "Error when executing PAC script function " + scriptEngine.getJsMainFunction().getJsFunctionName() + " : ", ex);
+            return Collections.singletonList(Proxy.NO_PROXY);
+        }
+    }
+
+    @Override
+    public boolean usesCaching() {
+        return (canUseURLCaching && (resultCache != null));
+    }
+
+    @Override
+    public String getJsEntryFunction() {
+        return scriptEngine.getJsMainFunction().getJsFunctionName();
+    }
+
+    @Override
+    public String getEngineInfo() {
+        ScriptEngineFactory factory = scriptEngine.getScriptEngine().getFactory();
+        return factory.getEngineName() + " version " + factory.getEngineVersion();
+    }
+
+    @Override
+    public String getPacScriptSource() {
+        return this.pacScriptSource;
+    }
+    
+    
+
+    private PacScriptEngine getScriptEngine(String pacSource) throws PacParsingException {
+
+        try {
+            String helperJSScript = getHelperJsScriptSource();
+            LOGGER.log(Level.FINER, "PAC Helper JavaScript :\n{0}", helperJSScript);
+            
+            ScriptEngine engine;
+            if (nashornJava8u40Available) {
+                engine = getNashornJSScriptEngine();
+            } else {
+                engine = getGenericJSScriptEngine();
+            }
+            
+            LOGGER.log(Level.FINE, "PAC script evaluator using:  {0}", getEngineInfo(engine));
+            
+            
+            PacHelperMethods pacHelpers = Lookup.getDefault().lookup(PacHelperMethods.class);
+            if (pacHelpers == null) { // this should be redundant but we take no chances
+                pacHelpers = new NbPacHelperMethods();
+            }
+            Bindings b = engine.createBindings();
+            b.put(JS_HELPER_METHODS_INSTANCE_NAME, pacHelpers);
+            engine.setBindings(b, ScriptContext.ENGINE_SCOPE);
+            
+            engine.eval(pacSource);
+            engine.eval(helperJSScript);
+
+            // Do some minimal testing of the validity of the PAC Script.
+            final PacJsEntryFunction jsMainFunction;
+            if (nashornJava8u40Available) {
+                jsMainFunction = testScriptEngine(engine, true);
+            } else {
+                jsMainFunction = testScriptEngine(engine, false);
+            }
+            
+            return new PacScriptEngine(engine, jsMainFunction);
+        } catch (ScriptException ex) {
+            throw new  PacParsingException(ex);
+        }
+    }
+    
+    private boolean getNashornJava8u40Available() {
+        try {
+            Class<?> klass = Class.forName("jdk.nashorn.api.scripting.NashornScriptEngineFactory");
+        } catch (ClassNotFoundException ex) {
+            return false;
+        }
+        return true;
+    }
+    
+    private ScriptEngine getNashornJSScriptEngine() {
+        NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
+        return factory.getScriptEngine(new ClassFilterPacHelpers());
+    }
+    
+    private ScriptEngine getGenericJSScriptEngine() {
+        // The result of the statements below may be Rhino, but more likely
+        // - since Java 8 - it will be a Nashorn engine.
+        ScriptEngineManager factory = new ScriptEngineManager();
+        return factory.getEngineByName("JavaScript");
+    }
+    
+    /**
+     * Test if the main entry point, function FindProxyForURL()/FindProxyForURLEx(), 
+     * is available.
+     */
+    private PacJsEntryFunction testScriptEngine(ScriptEngine eng, boolean doDeepTest) throws PacParsingException {
+        if (isJsFunctionAvailable(eng, PacJsEntryFunction.IPV6_AWARE.getJsFunctionName(), doDeepTest)) {
+            return PacJsEntryFunction.IPV6_AWARE;
+        }
+        if (isJsFunctionAvailable(eng, PacJsEntryFunction.STANDARD.getJsFunctionName(), doDeepTest)) {
+            return PacJsEntryFunction.STANDARD;
+        }
+        throw new PacParsingException("Function " + PacJsEntryFunction.STANDARD.getJsFunctionName() + " or " + PacJsEntryFunction.IPV6_AWARE.getJsFunctionName() + " not found in PAC Script.");
+    }
+
+    private boolean isJsFunctionAvailable(ScriptEngine eng, String functionName, boolean doDeepTest) {
+        // We want to test if the function is there, but without actually 
+        // invoking it.        
+        Object obj = eng.get(functionName);
+        
+        if (!doDeepTest && obj != null) {  
+            // Shallow test. We've established that there's
+            // "something" in the ENGINE_SCOPE with a name like
+            // functionName, and we *hope* it is a function, but we really don't
+            // know, therefore we call it a shallow test.
+            return true;
+        }
+        
+        // For Nashorn post JDK8u40 we can do even deeper validation
+        // using the ScriptObjectMirror class. This will not work for Rhino.
+        if (doDeepTest && obj != null) {
+            if (obj instanceof ScriptObjectMirror) {
+                    ScriptObjectMirror  som = (ScriptObjectMirror) obj;
+                    if (som.isFunction()) {
+                        return true;
+                    }
+            }
+        }        
+        return false;
+    }
+    
+
+    private String getHelperJsScriptSource() throws PacParsingException {
+        return HelperScriptFactory.getPacHelperSource(JS_HELPER_METHODS_INSTANCE_NAME);
+    }
+
+
+    /**
+     * Does the script source make reference to any of the date/time functions
+     * (timeRange(), dateRange(), weekdayRange()) ?
+     *
+     * @param pacScriptSource
+     * @return
+     */
+    private static boolean usesTimeDateFunctions(String pacScriptSource) {
+        // Will be called only once so there's little to be gained by precompiling 
+        // the regex statement.
+
+        Pattern pattern = Pattern.compile(".*(timeRange\\s*\\(|dateRange\\s*\\(|weekdayRange\\s*\\().*", Pattern.DOTALL);
+        Matcher matcher = pattern.matcher(pacScriptSource);
+        return matcher.matches();
+    }
+
+    private String getEngineInfo(ScriptEngine engine) {
+        StringBuilder sb = new StringBuilder();
+        ScriptEngineFactory f = engine.getFactory();
+        sb.append("LanguageName=");
+        sb.append("\"").append(f.getLanguageName()).append("\"");
+        sb.append(" ");
+        sb.append("LanguageVersion=");
+        sb.append("\"").append(f.getLanguageVersion()).append("\"");
+        sb.append(" ");
+        sb.append("EngineName=");
+        sb.append("\"").append(f.getEngineName()).append("\"");
+        sb.append(" ");
+        sb.append("EngineNameAliases=");
+        sb.append(Arrays.toString(f.getNames().toArray(new String[f.getNames().size()])));
+        sb.append(" ");
+        sb.append("EngineVersion=");
+        sb.append("\"").append(f.getEngineVersion()).append("\"");
+        return sb.toString();
+    }
+
+    /**
+     * Translates result from JavaScript into list of java proxy types.
+     * 
+     * The string returned from the JavaScript function (input to this
+     * method) can contain any number of the following building blocks, 
+     * separated by a semicolon:
+     * 
+     *   DIRECT
+     *       Connections should be made directly, without any proxies.
+     * 
+     *   PROXY host:port
+     *       The specified proxy should be used.
+     * 
+     *   SOCKS host:port
+     *       The specified SOCKS server should be used.
+     * 
+     * 
+     * @param uri
+     * @param proxiesString
+     * @return 
+     */
+    private List<Proxy> analyzeResult(URI uri, Object proxiesString) throws PacValidationException {
+        if (proxiesString == null) {
+            LOGGER.log(Level.FINE, "Null result for {0}", uri);
+            return null;
+        }
+        
+        StringTokenizer st = new StringTokenizer(proxiesString.toString(), ";"); //NOI18N
+        List<Proxy> proxies = new LinkedList<>();
+        while (st.hasMoreTokens()) {
+            String proxySpec = st.nextToken().trim();
+            proxies.add(getProxy(proxySpec));
+        }
+        return proxies;
+    }
+
+    private static Proxy getProxy(String proxySpec) throws PacValidationException {
+         if (proxySpec.equals(PAC_DIRECT)) {
+             return Proxy.NO_PROXY;
+         }      
+        
+        String[] ele = proxySpec.split(" +"); // NOI18N
+        if (ele.length != 2) {
+            throw new PacValidationException("The value \"" + proxySpec + "\" has incorrect format");
+        }
+        final Proxy.Type proxyType;
+        switch(ele[0]) {
+            case PAC_PROXY:
+            case PAC_HTTP_FFEXT:
+            case PAC_HTTPS_FFEXT:
+                proxyType = Proxy.Type.HTTP;
+                break;
+            case PAC_SOCKS:
+            case PAC_SOCKS4_FFEXT:
+            case PAC_SOCKS5_FFEXT:
+                proxyType = Proxy.Type.SOCKS;
+                break;
+            default:
+                throw new PacValidationException("The value \"" + ele[0] + "\" is an unknown proxy type");
+        }
+        
+        String hostAndPortNo = ele[1];
+        int i = hostAndPortNo.lastIndexOf(":"); // NOI18N
+        if (i <= 0 || i == (hostAndPortNo.length() - 1)) {
+            throw new PacValidationException("The string \"" + ele[1] + "\" has no port number");
+        }
+
+        String host = hostAndPortNo.substring(0, i);
+        String portStr = hostAndPortNo.substring(i+1);
+
+        int portNo = -1;
+        try {
+            portNo =  Integer.parseInt(portStr);
+        } catch (NumberFormatException ex) {
+            throw new PacValidationException("The portno value \"" + portStr + "\" cannot be converted to an integer");
+        }
+        
+        return new Proxy(proxyType, new InetSocketAddress(host, portNo));
+    }
+    
+    
+    private static class PacScriptEngine  {
+        private final ScriptEngine scriptEngine;
+        private final PacJsEntryFunction jsMainFunction;
+        private final Invocable invocable;
+
+        public PacScriptEngine(ScriptEngine scriptEngine, PacJsEntryFunction jsMainFunction) {
+            this.scriptEngine = scriptEngine;
+            this.jsMainFunction = jsMainFunction;
+            this.invocable = (Invocable) scriptEngine;
+        }
+
+        public PacJsEntryFunction getJsMainFunction() {
+            return jsMainFunction;
+        }
+
+        public ScriptEngine getScriptEngine() {
+            return scriptEngine;
+        }
+
+        public Invocable getInvocable() {
+            return invocable;
+        }
+        
+        public Object findProxyForURL(String url, String host) throws ScriptException, NoSuchMethodException {
+            return invocable.invokeFunction(jsMainFunction.getJsFunctionName(), url, host);
+        }
+    }
+        
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluatorFactory.java b/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluatorFactory.java
new file mode 100644
index 0000000..585e0e4
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/impl/NbPacScriptEvaluatorFactory.java
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.proxy.pac.impl;
+
+import org.netbeans.core.network.proxy.pac.PacScriptEvaluatorNoProxy;
+import org.netbeans.core.network.proxy.pac.PacParsingException;
+import org.netbeans.core.network.proxy.pac.PacScriptEvaluator;
+import org.netbeans.core.network.proxy.pac.PacScriptEvaluatorFactory;
+import org.openide.util.lookup.ServiceProvider;
+
+
+
+/**
+ * NetBeans default implementation of {@code PacScriptEvaluatorFactory}.
+ * 
+ * <p>Returns an instance of {@link NbPacScriptEvaluator}.
+ * 
+ * @author lbruun
+ */
+@ServiceProvider(service = PacScriptEvaluatorFactory.class)
+public class NbPacScriptEvaluatorFactory implements PacScriptEvaluatorFactory {
+
+    @Override
+    public PacScriptEvaluator createPacScriptEvaluator(String pacSource) throws PacParsingException {
+       
+        if (pacSource == null || pacSource.isEmpty()) {
+            return new PacScriptEvaluatorNoProxy();
+        }
+        return new NbPacScriptEvaluator(pacSource);
+    }
+
+    @Override
+    public PacScriptEvaluator getNoOpEvaluator() {
+           return new PacScriptEvaluatorNoProxy();
+    }
+}
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/impl/package-info.java b/core.network/src/org/netbeans/core/network/proxy/pac/impl/package-info.java
new file mode 100644
index 0000000..b5c69db
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/impl/package-info.java
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+/**
+ * NetBeans default implementation of a PAC script evaluation 
+ * environment.
+ * 
+ */
+package org.netbeans.core.network.proxy.pac.impl;
diff --git a/core.network/src/org/netbeans/core/network/proxy/pac/package-info.java b/core.network/src/org/netbeans/core/network/proxy/pac/package-info.java
new file mode 100644
index 0000000..6e76439
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/proxy/pac/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * Proxy Auto-Config (PAC) script related.
+ * 
+ * <p>
+ * A Proxy Auto-Config (PAC) script is a piece of JavaScript code
+ * which is typically downloaded from the network. It is meant to be
+ * evaluated on the client. The role of the PAC is to determine which 
+ * proxy to use for a given URL.
+ * 
+ * <p>
+ * Netscape wrote the original specification for the PAC script in 1995. The
+ * specification has never been ratified as a standard but is widely implemented
+ * on corporate Local Area Networks as a way to have a centralized and
+ * configurable proxy setup.
+ */
+package org.netbeans.core.network.proxy.pac;
diff --git a/core.network/src/org/netbeans/core/network/utils/HostnameUtils.java b/core.network/src/org/netbeans/core/network/utils/HostnameUtils.java
new file mode 100644
index 0000000..d974944
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/HostnameUtils.java
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils;
+
+import com.sun.jna.Platform;
+
+/**
+ * Utility functions for finding the local computer's name. 
+ * 
+ *
+ * @author lbruun
+ */
+public class HostnameUtils {
+
+    private HostnameUtils() {}
+    
+    
+    /**
+     * Gets the name which is likely to be the local host's primary
+     * name on the network.
+     * 
+     * <p>
+     * IMPLEMENTATION:
+     * <p>
+     * <ul>
+     *   <li>On Unix-like OSes (incl Mac OS X) this is the value as returned from
+     *       the {@code gethostname()} function from the standard C Library. </li>
+     *   <li>On Windows it is the value as returned from the
+     *       {@code gethostname()} function from {@code Ws2_32} library.
+     *       (without domain name). Note that this Windows function will do a 
+     *       name service lookup and the method is therefore potentially blocking, 
+     *       although it is more than likely that Windows has cached this result 
+     *       on computer startup in its DNS Client Cache and therefore the 
+     *       result will be returned very fast.</li>
+     * </ul>
+     * 
+     * @return host name
+     * @throws NativeException if there was an error executing the
+     *    system call.
+     */
+    public static String getNetworkHostname() throws NativeException {
+        switch(Platform.getOSType()) {
+            case Platform.WINDOWS:
+                return org.netbeans.core.network.utils.hname.win.HostnameUtilsWin.getHostName(true);
+            case Platform.MAC:
+                return org.netbeans.core.network.utils.hname.mac.HostnameUtilsMac.cLibGetHostname();
+            case Platform.LINUX:
+                return org.netbeans.core.network.utils.hname.linux.HostnameUtilsLinux.cLibGetHostname();
+            case Platform.SOLARIS:
+                return org.netbeans.core.network.utils.hname.solaris.HostnameUtilsSolaris.cLibGetHostname();
+            default:
+                return org.netbeans.core.network.utils.hname.unix.HostnameUtilsUnix.cLibGetHostname();
+        }
+    }
+
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/IpAddressUtils.java b/core.network/src/org/netbeans/core/network/utils/IpAddressUtils.java
new file mode 100644
index 0000000..50c3037
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/IpAddressUtils.java
@@ -0,0 +1,526 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.core.network.utils;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.regex.Pattern;
+import org.netbeans.api.annotations.common.NonNull;
+import org.openide.util.Exceptions;
+import org.openide.util.RequestProcessor;
+
+/**
+ * IP address utilities. Mainly providing functionality
+ * for doing name resolve with explicit timeout.
+ *
+ * <p>
+ * TODO: Support for reverse lookup with timeout isn't implemented. Hasn't been
+ * any need for it.
+ * 
+ * @author lbruun
+ */
+public class IpAddressUtils {
+
+    private static final Pattern IPV4_PATTERN = Pattern.compile("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$");
+    private static final RequestProcessor RP = new RequestProcessor("DNSBackgroundResolvers", 10);
+
+    private IpAddressUtils() {}
+    
+    /**
+     * Filters the result of a method according to IP protocol preference.
+     */
+    public enum IpTypePreference {
+        /**
+         * Only IPv4 address(es) in the returned value. 
+         */
+        IPV4_ONLY,
+        /**
+         * Only IPv6 address(es) in the returned value. 
+         */
+        IPV6_ONLY,
+        /**
+         * Any of IPv4 or IPv6 addresses are acceptable in the returned value,
+         * but IPv4 address is preferred over IPv6. If the method returns
+         * an array then IPv4 addresses will come before IPv6 addresses.
+         */
+        ANY_IPV4_PREF,
+        /**
+         * Any of IPv4 or IPv6 addresses are acceptable in the returned value,
+         * but IPv6 address is preferred over IPv4. If the method returns
+         * an array then IPv6 addresses will come before IPv4 addresses.
+         */
+        ANY_IPV6_PREF,
+        /**
+         * Any of IPv4 or IPv6 addresses are acceptable in the returned value,
+         * but their internal preference is determined by the setting in the
+         * JDK, namely the {@code java.net.preferIPv6Addresses} system property.
+         * If this property is {@code true} then using this preference will be
+         * exactly as {@link #ANY_IPV6_PREF}, if {@code false} it will be 
+         * exactly as {@link #ANY_IPV4_PREF}.
+         */
+        ANY_JDK_PREF
+    }
+    
+    /**
+     * Performs a name service lookup with a timeout. 
+     * 
+     * <p>This method can be used when the JRE's default DNS timeout is not
+     * acceptable. The method is essentially a wrapper around 
+     * {@link java.net.InetAddress#getAllByName(java.lang.String) InetAddress.getAllByName()}.
+     * 
+     * <p>A reasonable timeout value is 4 seconds (4000 ms) as this value
+     * will - with the JRE's default settings - allow each DNS server in a
+     * list of 4 servers to be queried once.
+     * 
+     * <p>If the {@code host} is a literal representation
+     * of an IPv4 address (using dot notation, for example {@code "192.168.1.44"}) 
+     * or an IPv6 address (any form accepted by {@link java.net.Inet6Address}),
+     * then the method will convert the text into {@code InetAddress} and 
+     * return immediately. The timeout does not apply to this case.
+     * 
+     * <br>
+     * <br>
+     * <p>
+     * <u>Java's default DNS timeout:</u>
+     * <p>
+     * The default timeout DNS lookup is described 
+     * <a href="http://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-dns.html#PROP">
+     * in the documentation for JNDI</a> in properties:
+     * <p>
+     * &nbsp;&nbsp;{@code com.example.jndi.dns.timeout.initial}  (defaults to 1 sec in Java 8)<br>
+     * &nbsp;&nbsp;{@code com.example.jndi.dns.timeout.retries}  (defaults to 4 in Java 8)
+     * <p>
+     * The defaults mean that if the host OS has two servers defined in its DNS
+     * server search string (usually there's <i>at least</i> two) then - if both
+     * servers do not respond - the call to
+     * {@link InetAddress#getByName(java.lang.String)} will block for
+     * <b>30 seconds!</b> before it returns. With three servers it will be 45 seconds
+     * and so forth. This wait may be unacceptable in some scenarios and this is 
+     * where this method is then a better alternative.
+     * <br>
+     * <br>
+     * @see #nameResolve(String, int, IpTypePreference) 
+     * @param host either a host name or a literal IPv4
+     *    address in dot notation.
+     * @param timeoutMs Milliseconds to wait for the result from the name service
+     *    query before aborting. A value of 0 means not to use any timeout for
+     *    the background resolver task. In that case only the standard timeouts
+     *    as defined in the JRE will apply.
+     * @param ipTypePref IP protocol filter
+     * @return the result of the name lookup (may be more than one address)
+     * @throws InterruptedException if the background task was interrupted
+     * @throws TimeoutException if the timeout ({@code timeoutMs}) expired
+     *    before a result was obtained.
+     * @throws UnknownHostException if no IP address for the host could be
+     *    found.
+     */
+    public static @NonNull InetAddress[] nameResolveArr(String host, int timeoutMs, IpTypePreference ipTypePref) 
+            throws InterruptedException, UnknownHostException, TimeoutException {
+        
+        if (looksLikeIpv6Literal(host) || looksLikeIpv4Literal(host)) {
+            // No DNS lookup is needed in this case so we can simply
+            // call directly. It won't block.
+            InetAddress addr = InetAddress.getByName(host);
+            if (ipTypePref == IpTypePreference.IPV4_ONLY  && addr instanceof Inet6Address) {
+                throw new UnknownHostException("Mismatch between supplied literal IP address \"" + host + "\" (which is IPv6) and value of ipTypePref : " + ipTypePref);
+            }
+            if (ipTypePref == IpTypePreference.IPV6_ONLY  && addr instanceof Inet4Address) {
+                throw new UnknownHostException("Mismatch between supplied literal IP address \"" + host + "\" (which is IPv6) and value of ipTypePref : " + ipTypePref);
+            }
+            return new InetAddress[]{addr};
+        }
+        
+        Callable<InetAddress[]> lookupTask = new DnsTimeoutTask(host);
+        Future<InetAddress[]> future = RP.submit(lookupTask);
+        try {
+            InetAddress[] ipAddresses;
+            if (timeoutMs == 0) {
+                ipAddresses = future.get();
+            } else {
+                ipAddresses = future.get(timeoutMs, TimeUnit.MILLISECONDS);
+            }
+            List<InetAddress> resultList = IpAddressUtilsFilter.filterInetAddresses(Arrays.asList(ipAddresses), ipTypePref);
+            if (resultList.isEmpty()) {
+                throw new UnknownHostException("A positive result was returned from name lookup for \"" + host + "\" but none that matched a filter of " + ipTypePref);
+            }
+            return resultList.toArray(new InetAddress[resultList.size()]);
+
+        } catch (ExecutionException ex) {
+            Throwable cause = ex.getCause();
+            if (cause instanceof UnknownHostException) {
+                throw (UnknownHostException) cause;
+            }
+            // Unlikely
+            Exceptions.printStackTrace(cause);
+            return new InetAddress[]{};
+        } catch (TimeoutException ex) {
+            // If the wait times out then cancel the background job too.
+            // We're no longer interested in the result.
+            // The downside of not letting the task finish is that the 
+            // JRE's DNS cache will then not be populated with the result
+            // - if a result is indeed obtained later than the timeout.
+            future.cancel(true);
+            throw new TimeoutException("No answer from name service within " + timeoutMs + " milliseconds when resolving \"" + host+ "\"");
+        } 
+    }
+    
+    /**
+     * Performs a name service lookup with a timeout. Same as 
+     * {@link #nameResolveArr(java.lang.String, int, org.netbeans.network.IpAddressUtils.IpTypePreference) nameResolveArr()}
+     * but only returns a single address. 
+     * 
+     * @see #nameResolveArr(String, int, IpTypePreference) 
+     * @param host either a host name or a literal IPv4
+     *    address in dot notation.
+     * @param timeoutMs Milliseconds to wait for the result from the name service
+     *    query before aborting. A value of 0 means not to use any timeout for
+     *    the background resolver task. In that case only the standard timeouts
+     *    as defined in the JRE will apply.
+     * @param ipTypePref IP protocol filter
+     * @return IP address
+     * @throws InterruptedException if the background task was interrupted
+     * @throws TimeoutException if the timeout ({@code timeoutMs}) expired
+     *    before a result was obtained.
+     * @throws UnknownHostException if no IP address for the host could be
+     *    found.
+     */
+    public static @NonNull InetAddress nameResolve(String host, int timeoutMs, IpTypePreference ipTypePref) 
+            throws InterruptedException, UnknownHostException, TimeoutException {
+        InetAddress[] ipAddresses = nameResolveArr(host, timeoutMs, ipTypePref);
+        // We're guaranteed the array will have length > 0 and never null,
+        // so the following is safe.
+        return ipAddresses[0];
+    }
+    
+    /**
+     * Performs a name service lookup with a timeout. Same as 
+     * {@link #nameResolveArr(java.lang.String, int, org.netbeans.network.IpAddressUtils.IpTypePreference) nameResolveArr()}
+     * but only returns a single address and uses 
+     * {@link IpTypePreference#ANY_JDK_PREF IpTypePreference.ANY_JDK_PREF}.
+     * 
+     * <p>
+     * There are several overloaded forms of this method. If you don't need
+     * any specific filtering on the type of IP address returned then use
+     * this method!
+     * 
+     * @see #nameResolveArr(String, int, IpTypePreference) 
+     * @param host either a host name or a literal IPv4
+     *    address in dot notation.
+     * @param timeoutMs Milliseconds to wait for the result from the name service
+     *    query before aborting. A value of 0 means not to use any timeout for
+     *    the background resolver task. In that case only the standard timeouts
+     *    as defined in the JRE will apply.
+     * @return IP address
+     * @throws InterruptedException if the background task was interrupted
+     * @throws TimeoutException if the timeout ({@code timeoutMs}) expired
+     *    before a result was obtained.
+     * @throws UnknownHostException if no IP address for the host could be
+     *    found.
+     */
+    public static @NonNull InetAddress nameResolve(String host, int timeoutMs) 
+            throws InterruptedException, UnknownHostException, TimeoutException {
+        return nameResolve(host, timeoutMs, IpTypePreference.ANY_JDK_PREF);
+    }
+    
+    /**
+     * Validates if a string can be parsed as an IPv4 address.
+     * 
+     * <p>
+     * The standard way to do this type of validation in Java is
+     * {@link java.net.InetAddress#getByName(java.lang.String)} but this method
+     * will block if the string is <i>not</i> an IP address literal, because it
+     * will query the name service. In contrast, this method relies solely on
+     * pattern matching techniques and will never block. Return value
+     * {@code true} is a guarantee that the string can be parsed as an IPv4
+     * address.
+     *
+     * @param ipAddressStr input string to be evaluated
+     * @return true if the string is a valid IPv4 literal on the 
+     *     form "#.#.#.#"
+     */
+    public static boolean isValidIpv4Address(String ipAddressStr) {
+        if (IPV4_PATTERN.matcher(ipAddressStr).matches()) {
+
+            String[] segments = ipAddressStr.split("\\.");
+
+            if (segments.length != 4) {
+                return false;
+            }
+            
+            for (String segment : segments) {
+                if (segment == null || segment.length() == 0) {
+                    return false;
+                }
+                
+                // leading zeroes are not allowed within the segment.
+                if (segment.length() > 1 && segment.startsWith("0")) {
+                    return false;
+                }
+                
+                try {
+                    int value = Integer.parseInt(segment);
+                    if (value > 255) {
+                        return false;
+                    }
+                } catch (NumberFormatException e) {
+                    return false;  // Unlikely. Already matched by regexp
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Does a shallow check if the argument looks like an IPv6 address as
+     * opposed to a host name. Note, that a return value of {@code true} doesn't
+     * guarantee that the argument is a <i>valid</i> IPv6 literal, but a return
+     * value of {@code false} is a guarantee that it is not.
+     * 
+     * <p>
+     * The method is not meant as a validity check. It is mainly useful
+     * for predicting if the JDK's {@code InetAddress#get*ByName()} will block 
+     * or not.
+     * 
+     * @param ipAddressStr
+     * @return true if argument looks like an IPv6 literal.
+     */
+    public static boolean looksLikeIpv6Literal(String ipAddressStr) {
+        if (ipAddressStr == null) {
+            return false;
+        }
+        // "::d" is the shortest possible form of an IPv6 address.
+        // and the longest form is 39 chars.
+        if (ipAddressStr.length() < 3 || ipAddressStr.length() > 39) {
+            return false;
+        }
+        if (ipAddressStr.startsWith(":") || ipAddressStr.endsWith(":")) {
+            return true;
+        }
+        // Matches A80::8:800:200C:417A or 0A80::8:800:200C:417A
+        if (ipAddressStr.length() >= 5) {
+            if ((ipAddressStr.charAt(3) == ':') || (ipAddressStr.charAt(4) == ':')) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * Does a shallow check if the argument looks like an IPv4 address as
+     * opposed to a host name. Note, that a return value of {@code true} doesn't
+     * guarantee that the argument is a <i>valid</i> IPv4 literal, but a return
+     * value of {@code false} is a guarantee that it is not.
+     *
+     * <p>
+     * The method is not meant as a validity check. It is mainly useful
+     * for predicting if the JDK's {@code InetAddress#get*ByName()} will block 
+     * or not.
+     * 
+     * <p>
+     * Note, the method will err on the side of caution meaning return {@code 
+     * false} if in doubt. Java allows "123" as a {@link java.net.Inet4Address 
+     * valid IPv4 address}, but in this case it cannot be determined if the
+     * input is a hostname or an IPv4 literal and therefore {@code false} is 
+     * returned.
+     *
+     * @see #isValidIpv4Address(java.lang.String) 
+     * @param ipAddressStr
+     * @return true if argument looks like an IPv4 literal
+     *     
+     */
+    public static boolean looksLikeIpv4Literal(String ipAddressStr) {
+        if (ipAddressStr == null || ipAddressStr.isEmpty()) {
+            return false;
+        }
+        if (!(isAsciiDigit(ipAddressStr.charAt(0)))) {
+            return false;
+        }
+        int dotPos = ipAddressStr.indexOf('.');
+        if (dotPos > 0 && dotPos < ipAddressStr.length()-1 && dotPos <=3) {
+            for(int i = 0; i < dotPos; i++) {
+                if (!(isAsciiDigit(ipAddressStr.charAt(i)))) {
+                    return false;
+                }
+            }
+            if (!(isAsciiDigit(ipAddressStr.charAt(dotPos+1)))) {
+                return false;
+            }
+        } else {
+            // No dot found or nothing after dot or the part before dot
+            // it too long
+            return false;
+        }
+        return true;
+    }
+    
+    /**
+     * Strips the domain part from a host name. Example: for {@code "foo.bar.com"}
+     * then {@code "foo"} will be returned.
+     * 
+     * <p>
+     * The method is safe to use even if the input is an IPv4 literal or IPv6
+     * literal. In this case the input will be returned unchanged.
+     *
+     * @param hostname
+     * @return hostname with domain stripped 
+     */
+    public static String removeDomain(String hostname) {
+        if (hostname == null) {
+            return hostname;
+        }
+        if (looksLikeIpv4Literal(hostname)) {
+            return hostname;
+        }
+
+        int pos = hostname.indexOf('.');
+        if (pos == -1) {
+            return hostname;
+        } else {
+            int posColon = hostname.indexOf(':');
+            if (posColon >=0 && posColon < pos) { // It is an IPv6 literal with an embedded IPv4 address
+                return hostname;
+            }
+            
+            return hostname.substring(0, pos);
+        }
+    }
+
+    /**
+     * Sorts a list of IP addresses.
+     * 
+     * @param addresses IP addresses
+     * @param ip4BeforeIp6 if IPv4 addresses are to come before IPv6 addresses 
+     *     ({@code true}) or not ({@code false}).
+     */
+    public static void sortIpAddresses(List<InetAddress> addresses, boolean ip4BeforeIp6) {
+        sortIpAddresses0(addresses, ip4BeforeIp6, false);
+    }
+    
+    /**
+     * Sorts a list of IP addresses, but only with respect to IPv4 vs IPv6. Addresses
+     * within the same class of protocol (e.g. IPv4 addresses) are not sorted.
+     * 
+     * @param addresses IP addresses
+     * @param ip4BeforeIp6 if IPv4 addresses are to come before IPv6 addresses 
+     *     ({@code true}) or not ({@code false}).
+     */
+    public static void sortIpAddressesShallow(List<InetAddress> addresses, boolean ip4BeforeIp6) {
+        sortIpAddresses0(addresses, ip4BeforeIp6, true);
+    }
+    
+    private static void sortIpAddresses0(List<InetAddress> addresses, boolean ip4BeforeIp6, boolean shallow) {
+        if (addresses != null && (addresses.size() > 1)) {
+            Collections.sort(addresses, new InetAddressComparator(ip4BeforeIp6, shallow));
+        }
+    }
+
+
+    
+    private static class DnsTimeoutTask implements Callable<InetAddress[]> {
+
+        private final String host;
+
+        public DnsTimeoutTask(String host) {
+            this.host = host;
+        }
+
+        @Override
+        public InetAddress[] call() throws UnknownHostException {
+            return InetAddress.getAllByName(host);
+        }
+    }
+    
+    
+    private static class InetAddressComparator implements Comparator<InetAddress> {
+
+        private final boolean ip4BeforeIp6;
+        private final boolean shallow;
+
+        public InetAddressComparator(boolean ip4BeforeIp6, boolean shallow) {
+            this.ip4BeforeIp6 = ip4BeforeIp6;
+            this.shallow = shallow;
+        }
+        
+        @Override
+        public int compare(InetAddress a1, InetAddress a2) {
+            byte[] bArr1 = a1.getAddress();
+            byte[] bArr2 = a2.getAddress();
+
+            if ((a1 instanceof Inet4Address) && (a2 instanceof Inet6Address)) {
+                return (ip4BeforeIp6) ? -1 : 1;
+            }
+            if ((a1 instanceof Inet6Address) && (a2 instanceof Inet4Address)) {
+                return (ip4BeforeIp6) ? 1 : -1;
+            }
+            
+            if (bArr1.length != bArr2.length) {
+                // according to JDK spec, this shouldn't be possible
+                // but we take no risks here. This could happen if one day
+                // there are more sub-classes of InetAddress than just 
+                // Inet4Address or Inet6Address.
+                if (bArr1.length < bArr2.length) {
+                    return (ip4BeforeIp6) ? -1 : 1;
+                } else {
+                    return (ip4BeforeIp6) ? 1 : -1;
+                }
+            }
+            
+            if (shallow) {
+                return 0;
+            }
+            
+            // Compare byte-by-byte.
+            for (int i = 0; i < bArr1.length; i++) {
+                int x1 = Byte.toUnsignedInt(bArr1[i]);
+                int x2 = Byte.toUnsignedInt(bArr2[i]);
+                
+                if (x1 == x2) {
+                    continue;
+                }
+                if (x1 < x2) {
+                    return -1;
+                } else {
+                    return 1;
+                }
+            }
+            return 0;
+        }
+    }
+    
+    private static boolean isAsciiDigit(char c) {
+        // Why doesn't the JDK have a shorthand for this?
+        return c >= 48 && c <= 57;
+    }
+
+    
+
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/IpAddressUtilsFilter.java b/core.network/src/org/netbeans/core/network/utils/IpAddressUtilsFilter.java
new file mode 100644
index 0000000..5fa0a7b
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/IpAddressUtilsFilter.java
@@ -0,0 +1,107 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+import org.netbeans.api.annotations.common.NonNull;
+
+/**
+ * Package private methods for choosing IP addresses from a
+ * list based on a stated preference.
+ * 
+ * @author lbruun
+ */
+class IpAddressUtilsFilter {
+
+    private static final boolean JDK_PREFER_IPV6_ADDRESS;
+    static {
+        JDK_PREFER_IPV6_ADDRESS = java.security.AccessController.doPrivileged(
+                new PrivilegedAction<Boolean>() {
+            @Override
+            public Boolean run() {
+                return Boolean.getBoolean("java.net.preferIPv6Addresses");
+
+            }
+        });
+    }
+    private IpAddressUtilsFilter() {}
+    
+    protected static InetAddress pickInetAddress(Iterable<InetAddress> sortedList, IpAddressUtils.IpTypePreference ipTypePref) {
+        IpAddressUtils.IpTypePreference pref = getIpTypePreferenceResolved(ipTypePref);
+        for (InetAddress ipAddress : sortedList) {
+            if (pref == IpAddressUtils.IpTypePreference.ANY_IPV4_PREF  || pref == IpAddressUtils.IpTypePreference.ANY_IPV6_PREF) {
+                return ipAddress;
+            }
+            if (ipAddress instanceof Inet4Address) {
+                if (pref == IpAddressUtils.IpTypePreference.IPV4_ONLY) {
+                    return ipAddress;
+                }
+            }
+            if (ipAddress instanceof Inet6Address) {
+                if (pref == IpAddressUtils.IpTypePreference.IPV6_ONLY) {
+                    return ipAddress;
+                }
+            }
+        }
+        return null;
+    }
+    
+    protected static @NonNull List<InetAddress> filterInetAddresses(Iterable<InetAddress> list, IpAddressUtils.IpTypePreference ipTypePref) {
+        IpAddressUtils.IpTypePreference pref = getIpTypePreferenceResolved(ipTypePref);
+        List<InetAddress> newList = new ArrayList<>();
+        if (list != null) {
+            for (InetAddress ipAddress : list) {
+                if (pref == IpAddressUtils.IpTypePreference.ANY_IPV4_PREF || pref == IpAddressUtils.IpTypePreference.ANY_IPV6_PREF) {
+                    newList.add(ipAddress);
+                } else {
+                    if ((ipAddress instanceof Inet4Address) && (pref == IpAddressUtils.IpTypePreference.IPV4_ONLY)) {
+                        newList.add(ipAddress);
+                    }
+                    if ((ipAddress instanceof Inet6Address) && (pref == IpAddressUtils.IpTypePreference.IPV6_ONLY)) {
+                        newList.add(ipAddress);
+                    }
+                }
+            }
+        }
+        if (pref == IpAddressUtils.IpTypePreference.ANY_IPV4_PREF) {
+            IpAddressUtils.sortIpAddressesShallow(newList,true);
+        }
+        if (pref == IpAddressUtils.IpTypePreference.ANY_IPV6_PREF) {
+            IpAddressUtils.sortIpAddressesShallow(newList,false);
+        }
+        return newList;
+    }
+
+    private static IpAddressUtils.IpTypePreference getIpTypePreferenceResolved(IpAddressUtils.IpTypePreference ipTypePref) {
+        if (ipTypePref == IpAddressUtils.IpTypePreference.ANY_JDK_PREF) {
+            if (JDK_PREFER_IPV6_ADDRESS) {
+                return IpAddressUtils.IpTypePreference.ANY_IPV6_PREF;
+            } else {
+                return IpAddressUtils.IpTypePreference.ANY_IPV4_PREF;
+            }
+        } else {
+            return ipTypePref;
+        }
+    }    
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/LocalAddressUtils.java b/core.network/src/org/netbeans/core/network/utils/LocalAddressUtils.java
new file mode 100644
index 0000000..0fa3713
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/LocalAddressUtils.java
@@ -0,0 +1,536 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.core.network.utils;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.core.network.utils.IpAddressUtils.IpTypePreference;
+import org.openide.util.RequestProcessor;
+
+/**
+ * Methods for determining the local host's own address. The methods provide
+ * two benefits over the core JDK classes.:
+ * <p>
+ * <ul>
+ *    <li><i>Caching</i>. Results from the methods are cached and can therefore
+ *       be returned without blocking. An application should call 
+ *       {@link #warmUp()} during the application's startup phase
+ *       so that results are readily available when needed.
+ *    </li>
+ *    <li><i>IP protocol preference</i>. All methods allow to state
+ *       an explicit preference for IPv4 vs IPv6. (<i>without</i> relation
+ *       to the JVM's overall protocol preference settings).
+ *    </li>
+ * </ul>
+ * 
+ * <p>
+ * The main method is 
+ * {@link #getMostLikelyLocalInetAddress(IpAddressUtils.IpTypePreference) getMostLikelyLocalInetAddress()}.
+ * The other methods essentially exist to provide input to this method but are
+ * exposed nevertheless if anyone wants them.
+ *  
+ * <p>
+ * These utility methods are in particular relevant in relation to
+ * the PAC helper method {@code myIpAddress()}. However, the class may indeed 
+ * be used for any use case.
+ * 
+ * <p>
+ * Note, that there is no single correct answer to the question about
+ * determining the local host's IP address in an environment with multiple
+ * network interfaces or multiple addresses on each network interface.
+ *
+ * @author lbruun
+ */
+public class LocalAddressUtils {
+    private static final Logger LOG = Logger.getLogger(LocalAddressUtils.class.getName());
+    private static final RequestProcessor RP = new RequestProcessor("LocalNetworkAddressFinder", 3);
+    
+    private static final byte[] LOOPBACK_IPV4_RAW = new byte[]{0x7f,0x00,0x00,0x01};
+    private static final byte[] LOOPBACK_IPV6_RAW = new byte[]{
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
+    private static InetAddress LOOPBACK_IPV4;
+    private static InetAddress LOOPBACK_IPV6;
+    static {
+        try {
+            LOOPBACK_IPV4 = InetAddress.getByAddress("local-ipv4-dummy", LOOPBACK_IPV4_RAW);
+            LOOPBACK_IPV6 = InetAddress.getByAddress("local-ipv6-dummy", LOOPBACK_IPV6_RAW);
+        } catch (UnknownHostException ex) {
+        }
+    }
+            
+   
+    private static final Object LOCK = new Object();  
+    private static Future<InetAddress> fut1;
+    private static Future<InetAddress[]> fut2;
+    private static Future<List<InetAddress>> fut3;
+
+    private static final Callable<InetAddress> C1 = new Callable(){
+        @Override
+        public InetAddress call() throws UnknownHostException  {
+            return InetAddress.getLocalHost();
+        }
+    };
+    private static final Callable<InetAddress[]> C2 = new Callable(){
+        @Override
+        public InetAddress[] call() throws UnknownHostException  {
+            try {
+                String hostname = HostnameUtils.getNetworkHostname();
+                return InetAddress.getAllByName(hostname);
+            } catch (NativeException ex) {
+                throw new UnknownHostException(ex.getMessage() + ", error code : " + ex.getErrorCode());
+            }
+        }
+    };
+    private static final Callable<List<InetAddress>> C3 = new Callable(){
+        @Override
+        public List<InetAddress> call() {
+            return getLocalNetworkInterfaceAddr();
+        }
+    };
+    static {
+        refreshNetworkInfo(false);
+    }
+    
+    
+    private LocalAddressUtils() {
+    }
+    
+
+    /**
+     * Starts collecting network information in a background task. This method
+     * is ideal for calling once during application startup phase. Calling this
+     * method during startup means that later calls to methods in this class
+     * will perform very fast, because they will then return a cached result and
+     * thus will never block.
+     */
+    public static void warmUp() {
+        // Does nothing. The refreshNetworkInfo task will be fired when the class
+        // is loaded by the classloaded.
+    }
+    
+    /**
+     * Refreshes the cached network information. Normally there is no reason
+     * to call this method as a computer's network information is unlikely
+     * to change during the lifetime of the application, unless the computer
+     * connects to a different network than was the case when the information
+     * was first cached.
+     * 
+     * @param await if true, waits for the refresh to complete
+     */
+    public static void refreshNetworkInfo(boolean await) {
+        synchronized (LOCK) {
+            fut1 = RP.submit(C1);
+            fut2 = RP.submit(C2);
+            fut3 = RP.submit(C3);
+            if (await) {
+                try {
+                    fut1.get();
+                    fut2.get();
+                    fut3.get();
+                } catch (InterruptedException | ExecutionException ex) {
+                }
+            }
+        }
+    }
+    
+    /**
+     * Returns the address of the local host.
+     * 
+     * <p>This method returns a cached result of calling 
+     * {@link InetAddress#getLocalHost()} and is therefore likely not to
+     * block (unlike the underlying method) unless this is the first time 
+     * this class is being referenced.
+     * 
+     * <p>Note that {@code InetAddress#getLocalHost()} is known to return
+     * unpredictable results for hosts with multiple network adapters. The
+     * {@link #getMostLikelyLocalInetAddresses(IpAddressUtils.IpTypePreference) getMostLikelyLocalInetAddresses()}
+     * method is much more likely to return an acceptable result.
+     * 
+     * @see #getMostLikelyLocalInetAddresses(IpAddressUtils.IpTypePreference) 
+     * @return
+     * @throws UnknownHostException 
+     */
+    public static @NonNull InetAddress getLocalHost() throws UnknownHostException {
+        synchronized (LOCK) {
+            if (fut1 == null) {
+                refreshNetworkInfo(false);
+            }
+            try {
+                return fut1.get();
+            } catch (ExecutionException ex) {
+                if (ex.getCause() instanceof UnknownHostException) {
+                    throw (UnknownHostException) ex.getCause();
+                }
+                throw new RuntimeException(ex.getCause());
+            } catch (InterruptedException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+    }
+
+    /**
+     * Returns the addresses of the local host.
+     * 
+     * <p>This is achieved by retrieving the
+     * {@link org.netbeans.network.hname.HostnameUtils#getNetworkHostname() name-of-the-host} 
+     * from the system, then resolving that name into a list of {@code InetAddress}es. 
+     * 
+     * <p>This method returns a cached result and is therefore likely not to
+     * block unless this is the first time this class is being referenced.
+     * 
+     * @see org.netbeans.network.hname.HostnameUtils#getNetworkHostname()
+     * @see InetAddress#getAllByName(java.lang.String) 
+     * @param ipTypePref filter
+     * @return
+     * @throws UnknownHostException if no IP address for the host name could be found
+     */
+    public static @NonNull InetAddress[] getLocalHostAddresses(IpTypePreference ipTypePref) throws UnknownHostException {
+        synchronized (LOCK) {
+            if (fut2 == null) {
+                refreshNetworkInfo(false);
+            }
+            try {
+                return fut2.get();
+            } catch (ExecutionException ex) {
+                if (ex.getCause() instanceof UnknownHostException) {
+                    throw (UnknownHostException) ex.getCause();
+                }
+                if (ex.getCause() instanceof SecurityException) {
+                    throw (SecurityException) ex.getCause();
+                }
+                throw new RuntimeException(ex.getCause());
+            } catch (InterruptedException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+    }    
+    
+    /**
+     * Returns a prioritized list of local host addresses. The further up
+     * on this list, the more likely it is that the address is the host's IP
+     * address.
+     * 
+     * <p>
+     * Prioritization is done on the following basis:
+     * <ul>
+     *   <li>IPv4 addresses are prioritized higher than IPv6 addresses.</li>
+     *   <li>Addresses belonging to a non-virtual interface are prioritized higher
+     *       than addresses belonging to a virtual interface.</li>
+     *   <li>Addresses belonging to an interface which supports multicast are
+     *       prioritized higher than addresses belonging to an interface which doesn't
+     *       support multicast.</li>
+     *   <li>Addresses belonging to an interface which 
+     *       {@link #isSoftwareVirtualAdapter(java.net.NetworkInterface) look
+     *       like a software virtual adapters} are prioritized lower than addresses 
+     *       belonging to interfaces which don't look software virtual adapters.</li>
+     *   <li>Addresses with a broadcast address are prioritized higher than
+     *       addresses with no broadcast address.</li>
+     * </ul>
+     * 
+     * <p>
+     * The method returns a cached result and is therefore likely not to block,
+     * unless this is the first time this class is being referenced.
+     * 
+     * @param ipTypePref filter
+     * @return prioritized list of addresses
+     */
+    public static @NonNull List<InetAddress> getPrioritizedLocalHostAddresses(IpAddressUtils.IpTypePreference ipTypePref)  {
+        synchronized (LOCK) {
+            if (fut3 == null) {
+                refreshNetworkInfo(false);
+            }
+            try {
+                return IpAddressUtilsFilter.filterInetAddresses(fut3.get(), ipTypePref);
+            } catch (ExecutionException ex) {
+                throw new RuntimeException(ex.getCause());
+            } catch (InterruptedException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+    }    
+    
+    /**
+     * Returns the host's IP addresses. Or rather the IP addresses most likely
+     * to be the ones the host is known by. This method is much more likely
+     * to return a correct result than the JDKs {@link InetAddress#getLocalHost()},
+     * in particular on hosts with multiple network interfaces or hosts
+     * that are virtualized or operating in a PaaS environment.
+     * 
+     * <p>
+     * The method uses the following prioritization for determining what
+     * to return, by continously moving to the next step if the previous 
+     * step yielded an empty result:
+     * <ol>
+     * <li>
+     * The list from {@link #getLocalHostAddresses(IpAddressUtils.IpTypePreference) getLocalHostAddresses() } 
+     * (List A) is compared to 
+     * {@link #getPrioritizedLocalHostAddresses(IpAddressUtils.IpTypePreference) getPrioritizedLocalHostAddresses() }
+     * (List B), 
+     * picking the ones from List B list which is also on List A.
+     * </li>
+     * 
+     * <li>
+     * Use List B.
+     * </li>
+     * 
+     * <li>
+     * Use List A.
+     * </li>
+     * 
+     * <li>
+     * Use the result from {@link #getLocalHost()} if it matches the 
+     * {@code ipTypePref} filter.
+     * </li>
+     * 
+     * <li>
+     * Finally, if everything else fails, return the result of 
+     * {@link #getLoopbackAddress(IpAddressUtils.IpTypePreference) 
+     * getLoopbackAddress()}.
+     * </li>
+     * </ol>
+     * 
+     * <p>
+     * The method uses the other methods in the class and is therefore 
+     * likely not to block, unless this is the first time this class is being 
+     * referenced.
+     * 
+     * @see #getMostLikelyLocalInetAddress(IpAddressUtils.IpTypePreference) 
+     * @param ipTypePref IP protocol filter
+     * @return IP addresses, never null
+     */
+    public static @NonNull InetAddress[] getMostLikelyLocalInetAddresses(IpAddressUtils.IpTypePreference ipTypePref) {
+        List<InetAddress> filteredList = getPrioritizedLocalHostAddresses(ipTypePref);
+        try {
+            List<InetAddress> localHostAddresses = Arrays.asList(getLocalHostAddresses(ipTypePref));
+            
+            if (localHostAddresses != null && !localHostAddresses.isEmpty()) {
+                List<InetAddress> tmpList = new ArrayList<>(5);
+                for (InetAddress addr : filteredList) {
+                    if (localHostAddresses.contains(addr)) {
+                        tmpList.add(addr);
+                    }
+                }
+
+                // #1 
+                if (!tmpList.isEmpty()) {
+                    return tmpList.toArray(new InetAddress[tmpList.size()]);
+                }
+
+                // #2
+                return localHostAddresses.toArray(new InetAddress[localHostAddresses.size()]);
+            }
+        } catch (UnknownHostException ex) {
+        }
+         
+        // #3
+        if (!filteredList.isEmpty()) {
+            return filteredList.toArray(new InetAddress[filteredList.size()]);
+        }
+            
+            
+        // #4
+        try {
+            InetAddress addr = IpAddressUtilsFilter.pickInetAddress(Collections.singletonList(getLocalHost()), ipTypePref);
+            if (addr != null) {
+                return new InetAddress[]{addr};
+            }
+        } catch (UnknownHostException ex) {
+        }
+        
+        // #5 - last resort
+        return new InetAddress[]{getLoopbackAddress(ipTypePref)};        
+    }
+    
+    /**
+     * Returns the host's IP address. Same as 
+     * {@link #getMostLikelyLocalInetAddresses(IpAddressUtils.IpTypePreference) getMostLikelyLocalInetAddresses()}
+     * but only returns a single IP address.
+     * 
+     * 
+     * @see #getMostLikelyLocalInetAddresses(IpAddressUtils.IpTypePreference) 
+     * @param ipTypePref IP protocol filter
+     * @return IP address, never null
+     */
+    public static @NonNull InetAddress getMostLikelyLocalInetAddress(IpAddressUtils.IpTypePreference ipTypePref) {
+        InetAddress[] ipAddresses = getMostLikelyLocalInetAddresses(ipTypePref);
+        // We're guaranteed the array will have length > 0 and never null.
+        return ipAddresses[0];
+    }
+
+    
+    /**
+     * Returns the loopback address.
+     * 
+     * <p>This method is similar to {@link InetAddress#getLoopbackAddress()} 
+     * except that the preference for IPv4 vs IPv6 can be explicitly 
+     * stated.
+     * 
+     * <p>For IPv4 the returned address is always {@code 127.0.0.1} and for
+     * IPv6 it is {@code ::1}.
+     * 
+     * @param ipTypePref IPv4 vs IP4v6 preference
+     * @return 
+     */
+    public static @NonNull InetAddress getLoopbackAddress(IpAddressUtils.IpTypePreference ipTypePref) {
+        switch(ipTypePref) {
+            case IPV4_ONLY:
+            case ANY_IPV4_PREF:
+                return LOOPBACK_IPV4;
+            case IPV6_ONLY:
+            case ANY_IPV6_PREF:
+                return LOOPBACK_IPV6;
+            default:    
+                return LOOPBACK_IPV4;
+        }
+    }
+    
+ 
+    private static @NonNull List<InetAddress> getLocalNetworkInterfaceAddr() {
+        final Map<InetAddress, Integer> mapWithScores = new HashMap<>();
+
+        // Looping through all network interfaces on the host.
+        // WARNING:  On Windows this is quite slow. On my Intel Core i7 it takes 
+        // approximately 1200 msecs and I only have 3 network interfaces defined. 
+        // The reason is that Windows creates a lot of virtual/bogus network 
+        // interfaces. (nope, you don't see them with 'ipconfig /all' command)
+        // On my Win10 host there are 46 entries returned from 
+        // NetworkInterface.getNetworkInterfaces() !! (all but a few are really 
+        // to be ignored). It is the actual call to NetworkInterface.getNetworkInterfaces() 
+        // which consumes 95% of the total time of this method.
+        // For a GUI oriented system a call to NetworkInterface.getNetworkInterfaces() 
+        // can make the application seem slow if it is done on a critical thread.
+        Enumeration<NetworkInterface> interfaces;
+        try {
+            interfaces = NetworkInterface.getNetworkInterfaces(); // expensive call
+        } catch (SocketException ex) {
+            LOG.log(Level.WARNING, "Cannot get host's network interfaces", ex);
+            return Collections.emptyList();
+        }
+
+        while (interfaces.hasMoreElements()) {
+            int ifScore = 0;  // the score for the interface, higher is better
+            NetworkInterface netIf = interfaces.nextElement(); // inexpensive call
+            try {
+                if (!netIf.isUp() || netIf.isLoopback()) {  // inexpensive call
+                    continue;  // discard
+                }
+                // Solaris note: When inside a non-global zone a network interface which
+                // is really virtual as seen from the global zone is seen as non-virtual
+                // from within the non-global zone. So, it is safe to give virtual
+                // interfaces a lower score.
+                if (netIf.isVirtual()) {
+                    ifScore -= 1;
+                }
+                if (!netIf.supportsMulticast()) {
+                    ifScore -= 1;
+                }
+                if (isSoftwareVirtualAdapter(netIf)) {
+                    ifScore -= 1;
+                }
+                    
+            } catch (SocketException ex) {
+                // isUp() and isLoopback() may throw exception. Discard
+                // the interface if that's the case.
+                continue;
+            }
+            List<InterfaceAddress> interfaceAddresses = netIf.getInterfaceAddresses(); // inexpensive call
+            for(InterfaceAddress ifAddr : interfaceAddresses) {
+                int addrScore = 0; // the score for the address, higher is better
+                InetAddress address = ifAddr.getAddress();
+                if (ifAddr.getBroadcast() == null) {
+                    addrScore -= 1;
+                }
+                if (address instanceof Inet6Address) {
+                    addrScore -= 1;
+                }
+                
+                mapWithScores.put(address, ifScore + addrScore);
+            }
+        }
+        
+        List<InetAddress> list = new ArrayList<>(mapWithScores.keySet());
+        
+        // Sort descending according to the scores 
+        Collections.sort( list, new Comparator<InetAddress>(){
+            @Override
+            public int compare(InetAddress o1, InetAddress o2) {
+                return mapWithScores.get(o2).compareTo(mapWithScores.get(o1));
+            }
+        });
+        
+        return list;  // returns a prioritized list
+    }
+    
+    
+   
+    
+   
+    
+    /**
+     * Tries to guess if the network interface is a virtual adapter
+     * by one of the makers of virtualization solutions (e.g. VirtualBox,
+     * VMware, etc). This is by no means a bullet proof method which is
+     * why it errs on the side of caution.
+     * 
+     * @param nif network interface
+     * @return 
+     */
+    public static boolean isSoftwareVirtualAdapter(NetworkInterface nif) {
+        
+        try {
+            // VirtualBox uses a semi-random MAC address for their adapter
+            // where the first 3 bytes are always the same:
+            //    Windows, Mac OS X, Linux : begins with 0A-00-27
+            //    Solaris:  begins with 10-00-27
+            // (above from VirtualBox source code)
+            //
+            byte[] macAddress = nif.getHardwareAddress();
+            if (macAddress != null & macAddress.length >= 3) {
+                if ((macAddress[0] == 0x0A || macAddress[0] == 0x08) &&
+                        (macAddress[1] == 0x00) &&
+                        (macAddress[2] == 0x27)) {                
+                    return true;
+                }
+            }
+            return false;
+        } catch (SocketException ex) {
+            return false;
+        }
+    }
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/NativeException.java b/core.network/src/org/netbeans/core/network/utils/NativeException.java
new file mode 100644
index 0000000..70f29fa
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/NativeException.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils;
+
+    /**
+     * A native error has occurred. (error in JNI or JNA call)
+     * 
+     * @author lbruun
+     */
+    public class NativeException extends Exception {
+
+        private final int errorCode;
+
+        public NativeException(int errorCode) {
+            this.errorCode = errorCode;
+        }
+
+        public NativeException(int errorCode, String msg) {
+            super(msg);
+            this.errorCode = errorCode;
+        }
+
+        public int getErrorCode() {
+            return errorCode;
+        }
+
+    }
diff --git a/core.network/src/org/netbeans/core/network/utils/SimpleObjCache.java b/core.network/src/org/netbeans/core/network/utils/SimpleObjCache.java
new file mode 100644
index 0000000..a7da2e6
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/SimpleObjCache.java
@@ -0,0 +1,153 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils;
+
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Simple cache with a max capacity. Encapsulates {@link java.util.Map Map}.
+ *
+ * <p>
+ * Eviction is based on usage. Every time a value is retrieved from the cache
+ * its 'usage timestamp' will change. When the cache is full, the value with the
+ * oldest usage timestamp will be evicted.
+ *
+ * <p>
+ * This class is thread-safe and is very efficient for read operations
+ * ({@code get}). The write operation ({@code put}) will - if the cache is full
+ * - have performance which degrades linearly with the size of the cache as all
+ * elements will have to be inspected to find the eviction candidate. For this
+ * reason, the class is best suited for smaller caches (say less than 1000
+ * elements).
+ *
+ *
+ * @author lbruun
+ * 
+ * @param <K> key 
+ * @param <V> value
+ */
+public class SimpleObjCache<K,V> {
+
+    private final int maxSize;
+    private final ConcurrentHashMap<K, ValueWrapper> map;
+    
+    /**
+     * Constructs a new cache with {@code maxSize} capacity. When the 
+     * cache is full, the value used/created the furthest in the past will
+     * be evicted to make room for a new element.
+     * 
+     * @param maxSize capacity - any value larger than zero
+     */
+    public SimpleObjCache(int maxSize) {
+        
+        if (maxSize <= 0) {
+            throw new IllegalArgumentException("Cache max size cannot be zero or less");
+        }
+        this.maxSize = maxSize;
+        this.map = new ConcurrentHashMap<>();
+    }
+    
+    /**
+     * Puts a value into the cache mapped to the specified key.
+     * 
+     * @param key key with which the specified value is to be associated
+     * @param value value to be associated with the specified key
+     * @return the previous value associated with key, or null if there 
+     *         was no mapping for the key
+     */
+     public V put(K key, V value) {
+        evictIfFull();
+        ValueWrapper existingValue = map.put(key, new ValueWrapper(value));
+        if (existingValue != null) {
+            return existingValue.value;
+        } else {
+            return null;
+        }
+    }
+    
+    /**
+     * Returns the value to which the specified key is mapped, or {@code null}
+     * if this cache contains no mapping for the key.
+     * 
+     * @param key the key whose associated value is to be returned
+     * @return  the value to which the specified key is mapped, or {@code null}
+     *          if this map contains no mapping for the key
+     */
+    public V get(K key) {
+        ValueWrapper wrapper = map.get(key);
+        if (wrapper != null) {
+            wrapper.lastUsed.set(System.currentTimeMillis());
+            return wrapper.value;
+        }
+        return null;
+    }
+    
+    /**
+     * Gets the current number of elements in the cache.
+     * @return 
+     */
+    public int getCacheSize() {
+        return map.size();
+    }
+    
+    /**
+     * Removes all elements from the cache. There is little reason to use
+     * this method as the cache will do its own house keeping.
+     */
+    public void clear() {
+        map.clear();
+    }
+    
+    synchronized private void evictIfFull() {
+        if (map.size() >= maxSize) {
+            K toBeEvicted = findEvictionCandidate();
+            if (toBeEvicted != null) {
+                map.remove(toBeEvicted);
+            }
+        }
+    }
+    
+    private K findEvictionCandidate() {
+        // Finds minimum of all timestamps of all elements in the cache.
+        
+        Optional<Map.Entry<K, ValueWrapper>> minEntry = map.entrySet().stream()
+                .min(Comparator.comparingLong( e -> e.getValue().lastUsed.get()));
+        
+        // Did we actually find something to delete?  
+        // Theoretically this should always be so, but we are cautious
+        if (minEntry.isPresent()) {  
+            return minEntry.get().getKey();
+        } else {
+            return null;
+        }
+    }
+    
+    private class ValueWrapper {
+        private final AtomicLong lastUsed = new AtomicLong(System.currentTimeMillis());
+        private final V value;
+
+        public ValueWrapper(V value) {
+            this.value = value;
+        }
+    }
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/linux/HostnameUtilsLinux.java b/core.network/src/org/netbeans/core/network/utils/hname/linux/HostnameUtilsLinux.java
new file mode 100644
index 0000000..a8e891e
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/linux/HostnameUtilsLinux.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils.hname.linux;
+
+import org.netbeans.core.network.utils.NativeException;
+
+
+
+/**
+ * Host name utilities for Linux.
+ */
+public class HostnameUtilsLinux {
+    
+     
+    
+    /**
+     * Returns the result of {@code gethostname()} function 
+     * from the standard Unix/Linux C Library.
+     * 
+     * @return host name 
+     * @throws NativeException if there was an error executing 
+     *    the system call.
+     */
+    public static String cLibGetHostname() throws NativeException {
+        return org.netbeans.core.network.utils.hname.unix.HostnameUtilsUnix.cLibGetHostname();
+    }
+    
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/linux/package-info.java b/core.network/src/org/netbeans/core/network/utils/hname/linux/package-info.java
new file mode 100644
index 0000000..a3e017d
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/linux/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ *  Utility functions for finding the local computer's name (Linux).
+ */
+package org.netbeans.core.network.utils.hname.linux;
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/mac/HostnameUtilsMac.java b/core.network/src/org/netbeans/core/network/utils/hname/mac/HostnameUtilsMac.java
new file mode 100644
index 0000000..23b3b33
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/mac/HostnameUtilsMac.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils.hname.mac;
+
+import org.netbeans.core.network.utils.NativeException;
+
+
+
+/**
+ * Host name utilities for for Mac OS X.
+ */
+public class HostnameUtilsMac {
+    
+ 
+    /**
+     * Returns the result of {@code gethostname()} function 
+     * from the standard Unix/Linux C Library.
+     * 
+     * <p>
+     * Mac OS X has a "dynamic hostname" feature which means that the
+     * value returned by this method may be completely different from the
+     * value in the Mac's <i>System Preferences</i>. 
+     * 
+     * @return host name 
+     * @throws NativeException if there was an error executing 
+     *    the system call.
+     */
+    public static String cLibGetHostname() throws NativeException {
+        return org.netbeans.core.network.utils.hname.unix.HostnameUtilsUnix.cLibGetHostname();
+    }
+    
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/mac/package-info.java b/core.network/src/org/netbeans/core/network/utils/hname/mac/package-info.java
new file mode 100644
index 0000000..7b21e91
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/mac/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ *  Utility functions for finding the local computer's name (Mac OS X).
+ */
+package org.netbeans.core.network.utils.hname.mac;
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/solaris/HostnameUtilsSolaris.java b/core.network/src/org/netbeans/core/network/utils/hname/solaris/HostnameUtilsSolaris.java
new file mode 100644
index 0000000..09c5237
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/solaris/HostnameUtilsSolaris.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils.hname.solaris;
+
+import org.netbeans.core.network.utils.NativeException;
+
+
+
+/**
+ * Host name utilities for Solaris.
+ */
+public class HostnameUtilsSolaris {
+    
+    
+    /**
+     * Returns the result of {@code gethostname()} function 
+     * from the standard Unix/Linux C Library.
+     * 
+     * @return host name 
+     * @throws NativeException if there was an error executing 
+     *    the system call.
+     */
+    public static String cLibGetHostname() throws NativeException {
+        return org.netbeans.core.network.utils.hname.unix.HostnameUtilsUnix.cLibGetHostname();
+    }
+    
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/solaris/package-info.java b/core.network/src/org/netbeans/core/network/utils/hname/solaris/package-info.java
new file mode 100644
index 0000000..f07919b
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/solaris/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ *  Utility functions for finding the local computer's name (Solaris).
+ */
+package org.netbeans.core.network.utils.hname.solaris;
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/unix/CLib.java b/core.network/src/org/netbeans/core/network/utils/hname/unix/CLib.java
new file mode 100644
index 0000000..9419368
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/unix/CLib.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils.hname.unix;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+
+/**
+ * Standard C Library on Unix.
+ */
+interface CLib extends Library {
+
+    CLib INSTANCE = (CLib) Native.loadLibrary("c", CLib.class);
+
+    public int gethostname(byte[] hostname, int bufferSize);
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/unix/HostnameUtilsUnix.java b/core.network/src/org/netbeans/core/network/utils/hname/unix/HostnameUtilsUnix.java
new file mode 100644
index 0000000..81b2e4c
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/unix/HostnameUtilsUnix.java
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils.hname.unix;
+
+import com.sun.jna.Native;
+import org.netbeans.core.network.utils.NativeException;
+
+/**
+ * Host name utilities for "general" Unix. 
+ */
+public class HostnameUtilsUnix {
+    
+    /**
+     * Max length of a Unix hostname including 
+     * null terminator.
+     */
+    private static final int MAXHOSTNAMELEN = 256;   
+    
+    
+    /**
+     * Returns the result of {@code gethostname()} function 
+     * from the standard Unix/Linux C Library.
+     * 
+     * @return host name 
+     * @throws NativeException if there was an error executing the
+     *    system call.
+     */
+    public static String cLibGetHostname() throws NativeException {
+        byte[] buf = new byte[MAXHOSTNAMELEN];
+        int retCode = CLib.INSTANCE.gethostname(buf, buf.length);
+        
+        if (retCode == 0) {
+            return Native.toString(buf);
+        }
+        throw new NativeException(retCode, "error calling 'gethostname()' function");
+    }
+    
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/unix/package-info.java b/core.network/src/org/netbeans/core/network/utils/hname/unix/package-info.java
new file mode 100644
index 0000000..3383850
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/unix/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ *  Utility functions for finding the local computer's name (generic Unix).
+ */
+package org.netbeans.core.network.utils.hname.unix;
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/win/HostnameUtilsWin.java b/core.network/src/org/netbeans/core/network/utils/hname/win/HostnameUtilsWin.java
new file mode 100644
index 0000000..340bdef
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/win/HostnameUtilsWin.java
@@ -0,0 +1,110 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils.hname.win;
+
+import com.sun.jna.Native;
+import com.sun.jna.platform.win32.Kernel32Util;
+import com.sun.jna.platform.win32.Win32Exception;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.core.network.utils.IpAddressUtils;
+import org.netbeans.core.network.utils.NativeException;
+
+/**
+ * Hostname utilities for Microsoft Windows OS.
+ */
+public class HostnameUtilsWin {
+    
+    private static final Logger LOGGER = Logger.getLogger(HostnameUtilsWin.class.getName());
+    
+    /**
+     * Gets the computer name.
+     * 
+     * <p>This is the also known as the NetBIOS name, although NetBIOS is 
+     * hardly used anymore. It is the same value as can be seen from the
+     * {@code COMPUTERNAME} environment variable.
+     * 
+     * <p>
+     * Windows API equivalent: {@code GetComputerName()} function from 
+     * {@code Kernel32} library.
+     * 
+     * @return computer name
+     * @throws NativeException if there was an error executing the
+     *    system call.
+     */
+    public static String getComputerName() throws NativeException {
+        try {
+            return Kernel32Util.getComputerName();
+        } catch (Win32Exception ex) {
+            LOGGER.log(Level.FINE, "Kernel32.GetComputerName error : {0}", ex.getHR().intValue());
+            String env = System.getenv("COMPUTERNAME");
+            if (env != null) {
+                return env;
+            }
+            throw new NativeException(ex.getHR().intValue(), "error calling 'GetComputerName()' function");
+        }
+    }
+
+    /**
+     * Gets the local host name.
+     * 
+     * <p>
+     * The underlying Windows function may return a simple host name (e.g.
+     * {@code chicago}) or it may return a fully qualified host name (e.g.
+     * {@code chicago.us.internal.net}). The {@code noQualify} parameter can
+     * remove the domain part if it exists.
+     *
+     * <p>
+     * Note that the underlying Windows function will do a name service lookup
+     * and the method is therefore potentially blocking, although it is more
+     * than likely that Windows has cached this result in the DNS Client Cache
+     * and therefore the result will be returned very fast.
+     *
+     * <p>
+     * Windows API equivalent: {@code gethostname()} function from 
+     * {@code Ws2_32} library.
+     * 
+     * @param noQualify if {@code true} the result is never qualified with domain,
+     *   if {@code false} the result is <i>potentially</i> a fully qualified
+     *   host name.
+     * 
+     * @return host name
+     * @throws NativeException if there was an error executing the
+     *    system call.
+     */
+    public static String getHostName(boolean noQualify) throws NativeException {
+        
+        byte[] buf = new byte[256];
+        
+        int returnCode = Winsock2Lib.INSTANCE.gethostname(buf, buf.length);
+        if (returnCode == 0) {
+            String result = Native.toString(buf);
+            if (noQualify) {
+                return IpAddressUtils.removeDomain(result);
+            } else {
+                return result;
+            }
+        } else {
+            throw new NativeException(returnCode, "error calling 'gethostname()' function");
+        }
+    }
+    
+
+    
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/win/Winsock2Lib.java b/core.network/src/org/netbeans/core/network/utils/hname/win/Winsock2Lib.java
new file mode 100644
index 0000000..c38be7a
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/win/Winsock2Lib.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils.hname.win;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+
+/**
+ * Winsock2 library on Windows
+ */
+public interface Winsock2Lib extends Library {
+
+    Winsock2Lib INSTANCE = (Winsock2Lib) Native.loadLibrary("ws2_32", Winsock2Lib.class);
+
+    public int gethostname(byte[] name, int namelen);
+}
diff --git a/core.network/src/org/netbeans/core/network/utils/hname/win/package-info.java b/core.network/src/org/netbeans/core/network/utils/hname/win/package-info.java
new file mode 100644
index 0000000..fa4cb98
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/hname/win/package-info.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ *  Utility functions for finding the local computer's name (Windows).
+ */
+package org.netbeans.core.network.utils.hname.win;
diff --git a/core.network/src/org/netbeans/core/network/utils/package-info.java b/core.network/src/org/netbeans/core/network/utils/package-info.java
new file mode 100644
index 0000000..dcb557e
--- /dev/null
+++ b/core.network/src/org/netbeans/core/network/utils/package-info.java
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Utilities related to network.
+ * 
+ */
+package org.netbeans.core.network.utils;
diff --git a/core.network/test/unit/data/pacFiles/wpad space.dat b/core.network/test/unit/data/pacFiles/wpad space.dat
index e010448..3310e6a 100644
--- a/core.network/test/unit/data/pacFiles/wpad space.dat	
+++ b/core.network/test/unit/data/pacFiles/wpad space.dat	
@@ -1,3 +1,17 @@
+/* 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 function FindProxyForURL(url, host)
 {
     // Make everything lower case.
diff --git a/core.network/test/unit/data/pacFiles/wpad.dat b/core.network/test/unit/data/pacFiles/wpad.dat
index e010448..3310e6a 100644
--- a/core.network/test/unit/data/pacFiles/wpad.dat
+++ b/core.network/test/unit/data/pacFiles/wpad.dat
@@ -1,3 +1,17 @@
+/* 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 function FindProxyForURL(url, host)
 {
     // Make everything lower case.
diff --git a/core.network/test/unit/data/pacFiles2/pac-test-sandbox-breakout.js b/core.network/test/unit/data/pacFiles2/pac-test-sandbox-breakout.js
new file mode 100644
index 0000000..fb4c654
--- /dev/null
+++ b/core.network/test/unit/data/pacFiles2/pac-test-sandbox-breakout.js
@@ -0,0 +1,32 @@
+/* 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+
+// 
+// A PAC script which tries to break out of the sandbox and attempts
+// to do something really nasty.
+//
+
+function FindProxyForURL(url, host)
+{
+    alert("pac-test-sandbox-breakout.js");
+    var JRuntime = Java.type("java.lang.Runtime");
+    JRuntime.getRuntime().exec("dosomethingnasty.exe");
+
+
+    
+    return "DIRECT";
+}
\ No newline at end of file
diff --git a/core.network/test/unit/data/pacFiles2/pac-test1.js b/core.network/test/unit/data/pacFiles2/pac-test1.js
new file mode 100644
index 0000000..715e408
--- /dev/null
+++ b/core.network/test/unit/data/pacFiles2/pac-test1.js
@@ -0,0 +1,33 @@
+/* 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+
+// 
+// A very simple PAC file
+// Always returns "DIRECT".
+//
+
+function FindProxyForURL(url, host)
+{
+
+    url = url.toLowerCase();
+    host = host.toLowerCase();
+
+    alert("This is pac-test1.js");
+
+    
+    return "DIRECT";
+}
\ No newline at end of file
diff --git a/core.network/test/unit/data/pacFiles2/pac-test2.js b/core.network/test/unit/data/pacFiles2/pac-test2.js
new file mode 100644
index 0000000..72a3ae6
--- /dev/null
+++ b/core.network/test/unit/data/pacFiles2/pac-test2.js
@@ -0,0 +1,33 @@
+/* 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+// 
+// A very simple PAC file
+// Returns a list of 3 proxies.
+//
+
+function FindProxyForURL(url, host)
+{
+
+
+
+
+    url = url.toLowerCase();
+    host = host.toLowerCase();
+    alert("This is pac-test2.js");
+
+
+    return "PROXY localhost:8081; PROXY localhost:8082; DIRECT";
+}
\ No newline at end of file
diff --git a/core.network/test/unit/data/pacFiles2/pac-test3.js b/core.network/test/unit/data/pacFiles2/pac-test3.js
new file mode 100644
index 0000000..d19f779
--- /dev/null
+++ b/core.network/test/unit/data/pacFiles2/pac-test3.js
@@ -0,0 +1,39 @@
+/* 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+// 
+// A very simple PAC file
+// Uses one of the time-sensitive functions (weekdayRange(), dateRange() or timeRange())
+// and will therefore imply no use of result caching.
+//
+
+function FindProxyForURL(url, host)
+{
+
+
+
+
+    url = url.toLowerCase();
+    host = host.toLowerCase();
+
+    alert("This is pac-test3.js");
+
+    if (weekdayRange("FRI"))
+        return "DIRECT";
+    else
+        return "PROXY localhost:8080";
+
+
+}
\ No newline at end of file
diff --git a/core.network/test/unit/data/pacFiles2/pac-test4.js b/core.network/test/unit/data/pacFiles2/pac-test4.js
new file mode 100644
index 0000000..0848639
--- /dev/null
+++ b/core.network/test/unit/data/pacFiles2/pac-test4.js
@@ -0,0 +1,242 @@
+/* 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+// 
+// PAC script which tests all helper functions.
+// All tests are "negative tests".  If something fails
+// the test that fails will be returned in the proxy name.
+// If everything works as expected, the value "DIRECT" will be returned.
+//
+
+function FindProxyForURL(url, host) {
+
+    alert("This is pac-test4.js");
+
+
+// 
+// isPlainHostName
+//
+
+    alert("isPlainHostName(): Doing tests...");
+  
+    if (!isPlainHostName("somehost"))
+        return "PROXY isPlainHostName:1";
+    if (isPlainHostName("somehost.dom1.com"))
+        return "PROXY isPlainHostName:2";
+
+
+//
+// dnsDomainIs
+//
+    alert("dnsDomainIs(): Doing tests...");
+
+    if (!dnsDomainIs("www.netscape.com", ".netscape.com"))
+        return "PROXY dnsDomainIs:1";
+    if (!dnsDomainIs("www.netscape.com", "netscape.com"))
+        return "PROXY dnsDomainIs:2";
+    if (dnsDomainIs("www.netscape.com", ".com"))
+        return "PROXY dnsDomainIs:3";
+    if (dnsDomainIs("www.netscape.com", "somethingelse.com"))
+        return "PROXY dnsDomainIs:4";
+    if (dnsDomainIs("www.netscape.com", ""))
+        return "PROXY dnsDomainIs:5";
+    if (dnsDomainIs("www.netscape.com", null))
+        return "PROXY dnsDomainIs:6";
+    
+// 
+// localHostOrDomainIs
+//
+    alert("localHostOrDomainIs(): Doing tests...");
+    
+    if (!localHostOrDomainIs("www.netscape.com", "www.netscape.com"))
+        return "PROXY localHostOrDomainIs:1";
+    if (!localHostOrDomainIs("www", "www.netscape.com"))
+        return "PROXY localHostOrDomainIs:2";
+    if (localHostOrDomainIs("www.netscape.com", "www.netscape.com2"))
+        return "PROXY localHostOrDomainIs:3";
+    if (localHostOrDomainIs("www1.netscape.com", "www2.netscape.com"))
+        return "PROXY localHostOrDomainIs:4";
+    if (localHostOrDomainIs("www1", "www2.netscape.com"))
+        return "PROXY localHostOrDomainIs:5";
+    
+//
+// isResolvable
+//
+    alert("isResolvable(): Doing tests...");
+    
+    if (!isResolvable("localhost"))
+        return "PROXY isResolvable:1";
+    if (!isResolvable("www.google.com"))   // will only work if we have access to Internet during test
+        return "PROXY isResolvable:2";
+    if (isResolvable("gsd4hgbnw5xa.kd9greey934.kod82r"))   
+        return "PROXY isResolvable:3";
+    
+//
+// dnsResolve
+//
+    alert("dnsResolve(): Doing tests...");
+    
+    if (!("127.0.0.1" === dnsResolve("localhost")))
+        return "PROXY dnsResolve:1";
+    if (!("8.8.8.8" === dnsResolve("google-public-dns-a.google.com"))) // will only work if we have access to Internet
+        return "PROXY dnsResolve:2";
+    
+//
+// myIpAddress
+//
+    alert("myIpAddress(): Doing tests...");
+    
+    var myIp = myIpAddress();
+    if ("127.0.0.1" === myIp)
+        return "PROXY myIpAddress:1";
+    
+//
+// isInNet
+//
+    alert("isInNet(): Doing tests...");
+    
+    if (!isInNet("localhost", "127.0.0.1", "255.255.255.255"))
+        return "PROXY isInNet:1";
+    if (!isInNet("google-public-dns-a.google.com", "8.8.8.8", "255.255.255.255"))
+        return "PROXY isInNet:2";
+    if (isInNet("192.168.1.3", "192.168.1.1", "255.255.255.255"))
+        return "PROXY isInNet:3";
+    if (!isInNet("192.168.1.3", "192.168.1.1", "255.255.255.0"))
+        return "PROXY isInNet:4";
+    if (!isInNet("192.168.1.1", "192.168.3.1", "255.255.0.255"))
+        return "PROXY isInNet:5";
+    if (!isInNet("10.10.10.10", "12.12.12.12", "0.0.0.0"))
+        return "PROXY isInNet:6";
+    if (isInNet("10.10.10.10", "12.12.12.12", "0.0.255.0"))
+        return "PROXY isInNet:7";
+
+//            
+// dnsDomainLevels
+//
+    alert("dnsDomainLevels(): Doing tests...");
+
+    if (!(2 === dnsDomainLevels("www.netscape.com")))
+        return "PROXY dnsDomainLevels:1";
+    if (!(0 === dnsDomainLevels("www")))
+        return "PROXY dnsDomainLevels:2";
+    if (!(1 === dnsDomainLevels("www.")))
+        return "PROXY dnsDomainLevels:3";
+
+
+//
+// shExpMatch
+// 
+    alert("shExpMatch(): Doing tests...");
+    
+    if (!shExpMatch("www.netscape.com", "*netscape*"))
+        return "PROXY shExpMatch:1";
+    if (!shExpMatch("www.netscape.com", "*net*"))
+        return "PROXY shExpMatch:2";
+    if (shExpMatch("www.netscape.com", "*google*"))
+        return "PROXY shExpMatch:3";
+    if (!shExpMatch("www.netscape.com", "www*"))
+        return "PROXY shExpMatch:4";
+
+//
+// weekdayRange
+//
+    alert("weekdayRange(): Doing tests...");
+    
+    if (!weekdayRange("MON", "SUN", "GMT"))
+        return "PROXY weekdayRange:1";
+    if (!weekdayRange("MON", "SUN", null))
+        return "PROXY weekdayRange:2";
+
+//
+// dateRange
+//
+    // Difficult to test from JavaScript side because it will depend on 
+    // date when test is executed.
+    // We test if current date is between some values it will always be
+    // in between, which isn't much of a test!
+    
+    alert("dateRange(): Doing tests...");
+    
+    if (!(dateRange(1998, 2199)))
+        return "PROXY dateRange:1";
+    if (!(dateRange(1998, 2199, "GMT")))
+        return "PROXY dateRange:2";
+    if (!(dateRange(1, 31)))
+        return "PROXY dateRange:3";
+    if (!(dateRange(1, 31, "GMT")))
+        return "PROXY dateRange:4";
+    if (!(dateRange("JAN", "DEC")))
+        return "PROXY dateRange:5";
+    if (!(dateRange("JAN", "DEC", "GMT")))
+        return "PROXY dateRange:6";
+    if (!(dateRange(1, "JAN", 31, "DEC")))
+        return "PROXY dateRange:7";
+    if (!(dateRange(1, "JAN", 31, "DEC", "GMT")))
+        return "PROXY dateRange:8";
+    if (!(dateRange(1, "JAN", 1998, 31, "DEC", 2199)))
+        return "PROXY dateRange:9";
+    if (!(dateRange(1, "JAN", 1998, 31, "DEC", 2199, "GMT")))
+        return "PROXY dateRange:10";
+
+//
+// timeRange
+//
+    alert("timeRange(): Doing tests...");
+    // Difficult to test from JavaScript side because it will depend on 
+    // time when test is executed.
+    // We test if current time is between 00:00:00 and 23:59:59, which
+    // isn't much of a test!
+    
+    if (!(timeRange(0, 0, 0, 23, 59, 59)))
+        return "PROXY timeRange:1";
+    if (!(timeRange(0, 0, 0, 23, 59, 59, "GMT")))
+        return "PROXY timeRange:2";
+    if (!(timeRange(0, 0, 23, 59)))
+        return "PROXY timeRange:3";
+    if (!(timeRange(0, 0, 23, 59, "GMT")))
+        return "PROXY timeRange:4";
+
+
+//
+// isResolvableEx
+//
+    alert("isResolvableEx(): Doing tests...");
+
+    if (!(isResolvableEx("localhost")))
+        return "PROXY isResolvableEx:1";
+
+//
+// dnsResolveEx
+//
+    alert("dnsResolveEx(): Doing tests...");
+
+    if (!(dnsResolveEx("dfg3qyuaz.g4yst5.gfw58703sd") === ""))
+        return "PROXY dnsResolveEx:1";
+
+//
+// myIpAddressEx
+//
+    alert("myIpAddressEx(): Doing tests...");
+
+    var myIpEx = myIpAddressEx();
+    if ((myIpEx === "127.0.0.1") || (myIpEx === "0:0:0:0:0:0:0:1") || (myIpEx === "::1"))
+        return "PROXY myIpAddressEx:1";
+
+
+    alert("pac-test4.js:  All tests passed");
+
+    return "DIRECT";
+
+}
\ No newline at end of file
diff --git a/core.network/test/unit/src/org/netbeans/core/network/proxy/ProxyAutoConfigTest.java b/core.network/test/unit/src/org/netbeans/core/network/proxy/ProxyAutoConfigTest.java
index 728d2f1..2be4197 100644
--- a/core.network/test/unit/src/org/netbeans/core/network/proxy/ProxyAutoConfigTest.java
+++ b/core.network/test/unit/src/org/netbeans/core/network/proxy/ProxyAutoConfigTest.java
@@ -88,13 +88,17 @@ public class ProxyAutoConfigTest extends NbTestCase {
             URI uri = pac.getPacURI();
             assertNotNull(uri);
             assertNull(uri.getHost());
-            List<Proxy> proxies = pac.findProxyForURL(new URI("http://netbeans.org"));
+            List<Proxy> proxies = pac.findProxyForURL(new URI("http://apache.org"));
             assertEquals(1, proxies.size());
-            assertTrue(pacFileLoc + ": " + proxies.get(0).toString(), proxies.get(0).toString().startsWith("HTTP @ www-proxy.us.oracle.com/"));
+            Proxy proxy = proxies.get(0);
+            assertEquals(pacFileLoc, Proxy.Type.HTTP, proxy.type());
+            assertEquals(pacFileLoc, "www-proxy.us.oracle.com:80", proxy.address().toString());
             
-            proxies = pac.findProxyForURL(new URI("https://netbeans.org"));
+            proxies = pac.findProxyForURL(new URI("https://apache.org"));
             assertEquals(1, proxies.size());
-            assertTrue(pacFileLoc + ": " + proxies.get(0).toString(), proxies.get(0).toString().startsWith("HTTP @ www-proxy.us.oracle.com/"));
+            proxy = proxies.get(0);
+            assertEquals(pacFileLoc, Proxy.Type.HTTP, proxy.type());
+            assertEquals(pacFileLoc, "www-proxy.us.oracle.com:80", proxy.address().toString());
         }
     }
     
diff --git a/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacEngineTest.java b/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacEngineTest.java
new file mode 100644
index 0000000..7d109a7
--- /dev/null
+++ b/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacEngineTest.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.Proxy;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.Collections;
+import java.util.List;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.netbeans.core.network.proxy.pac.impl.NbPacScriptEvaluatorFactory;
+import org.netbeans.junit.NbTestCase;
+
+/**
+ *
+ */
+public class PacEngineTest extends NbTestCase {
+    
+    public PacEngineTest() {
+        super("Pac Evaluator Test");
+    }
+    
+    @BeforeClass
+    public static void setUpClass() {
+    }
+    
+    @AfterClass
+    public static void tearDownClass() {
+    }
+    
+    @Before
+    public void setUp() {
+    }
+    
+    @After
+    public void tearDown() {
+    }
+
+   
+   
+
+    /**
+     * Test of toSemiColonListStr method, of class PacUtils.
+     */
+    @Test
+    public void testEngine() throws PacParsingException, IOException, URISyntaxException, PacValidationException {
+        System.out.println("toSemiColonListStr");
+
+        PacScriptEvaluatorFactory factory = new NbPacScriptEvaluatorFactory();
+        
+        testPacFile("pac-test1.js", factory, 1, true);
+        testPacFile("pac-test2.js", factory, 3, true);
+        testPacFile("pac-test3.js", factory, 1, false);
+        testPacFileMalicious("pac-test-sandbox-breakout.js", factory);
+
+        testPacFile2("pac-test4.js", factory);
+    }
+
+    private String getPacSource(String pacFileName) throws IOException {
+        File pacFilesDir = new File(getDataDir(), "pacFiles2");
+        byte[] b = Files.readAllBytes((new File(pacFilesDir, pacFileName)).toPath());
+        return new String(b, StandardCharsets.UTF_8);
+    }
+
+    private void testPacFile(String pacFileName, PacScriptEvaluatorFactory factory, int expectedReturnedProxies, boolean usesCaching) throws IOException, PacParsingException, URISyntaxException, PacValidationException {
+        String pacSource = getPacSource(pacFileName);
+
+        PacScriptEvaluator pacEvaluator = factory.createPacScriptEvaluator(pacSource);
+
+        URI testURL = new URI("http://netbeans.apache.org");  // doesn't actually matter which URL we use
+        List<Proxy> proxies = pacEvaluator.findProxyForURL(testURL);
+
+        assertEquals(expectedReturnedProxies, proxies.size());
+        assertEquals(usesCaching, pacEvaluator.usesCaching());
+    }
+
+    private void testPacFile2(String pacFileName, PacScriptEvaluatorFactory factory ) throws IOException, PacParsingException, URISyntaxException, PacValidationException {
+        String pacSource = getPacSource(pacFileName);
+
+        PacScriptEvaluator pacEvaluator = factory.createPacScriptEvaluator(pacSource);
+
+        URI testURL = new URI("http://netbeans.apache.org");  // doesn't actually matter which URL we use
+        List<Proxy> proxies = pacEvaluator.findProxyForURL(testURL);
+
+        assertEquals(Collections.singletonList(Proxy.NO_PROXY), proxies);
+    }
+    
+    private void testPacFileMalicious(String pacFileName, PacScriptEvaluatorFactory factory ) throws IOException, PacParsingException, URISyntaxException, PacValidationException {
+        String pacSource = getPacSource(pacFileName);
+
+        PacScriptEvaluator pacEvaluator = factory.createPacScriptEvaluator(pacSource);
+
+        URI testURL = new URI("http://netbeans.apache.org");  // doesn't actually matter which URL we use
+        List<Proxy> proxies = pacEvaluator.findProxyForURL(testURL);
+
+        assertEquals(Collections.singletonList(Proxy.NO_PROXY), proxies);
+    }
+}
diff --git a/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacUtilsDateTimeTest.java b/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacUtilsDateTimeTest.java
new file mode 100644
index 0000000..3734f81
--- /dev/null
+++ b/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacUtilsDateTimeTest.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.netbeans.core.network.proxy.pac.datetime.PacUtilsDateTime;
+
+/**
+ *
+ */
+public class PacUtilsDateTimeTest {
+    
+    public PacUtilsDateTimeTest() {
+    }
+    
+    @BeforeClass
+    public static void setUpClass() {
+    }
+    
+    @AfterClass
+    public static void tearDownClass() {
+    }
+    
+    @Before
+    public void setUp() {
+    }
+    
+    @After
+    public void tearDown() {
+    }
+
+    /**
+     * Test of isInWeekdayRange method, of class PacUtilsDateTime.
+     */
+    @Test
+    public void testIsInWeekdayRange() throws Exception {
+        System.out.println("isInWeekdayRange");
+        Calendar cal = Calendar.getInstance();
+        int weekdayNum = cal.get(Calendar.DAY_OF_WEEK);
+        Date[] dates = new Date[7];
+        dates[weekdayNum-1] = cal.getTime();
+        for(int i=weekdayNum+1; i <= 7; i++) {
+            cal.add(Calendar.DATE, 1);
+            dates[i-1] = cal.getTime();
+        }
+        cal = Calendar.getInstance();
+        for(int i=weekdayNum-1; i >=1; i--) {
+            cal.add(Calendar.DATE, -1);
+            dates[i-1] = cal.getTime();
+        }
+        
+        Date nowSUN = dates[0];
+        Date nowMON = dates[1];
+        Date nowTUE = dates[2];
+        Date nowWED = dates[3];
+        Date nowTHU = dates[4];
+        Date nowFRI = dates[5];
+        Date nowSAT = dates[6];
+
+        assertTrue(PacUtilsDateTime.isInWeekdayRange(nowMON, "MON"));
+        assertFalse(PacUtilsDateTime.isInWeekdayRange(nowMON, "SUN"));
+        assertTrue(PacUtilsDateTime.isInWeekdayRange(nowMON, "MON", "SUN"));
+        assertTrue(PacUtilsDateTime.isInWeekdayRange(nowSAT, "FRI", "TUE"));
+        assertTrue(PacUtilsDateTime.isInWeekdayRange(nowMON, "FRI", "TUE"));
+        assertFalse(PacUtilsDateTime.isInWeekdayRange(nowTHU, "FRI", "TUE"));
+        assertFalse(PacUtilsDateTime.isInWeekdayRange(nowTHU, "SAT", "MON"));
+        assertTrue(PacUtilsDateTime.isInWeekdayRange(nowSUN, "SAT", "MON", "GMT"));
+        assertFalse(PacUtilsDateTime.isInWeekdayRange(nowSUN, "MON", "TUE", "GMT"));
+        
+    }
+
+    /**
+     * Test of isInTimeRange method, of class PacUtilsDateTime.
+     */
+    @Test
+    public void testIsInTimeRange() throws Exception {
+        System.out.println("isInTimeRange");
+        
+        SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
+        
+        assertTrue(PacUtilsDateTime.isInTimeRange(df.parse("13:00:00"), 13));
+        assertFalse(PacUtilsDateTime.isInTimeRange(df.parse("14:00:00"), 13));
+        assertTrue(PacUtilsDateTime.isInTimeRange(df.parse("14:00:00"), 13, 14));
+        assertFalse(PacUtilsDateTime.isInTimeRange(df.parse("14:01:00"), 13, 14));
+        assertTrue(PacUtilsDateTime.isInTimeRange(df.parse("14:00:00"), 13, 30, 14, 30));
+        assertFalse(PacUtilsDateTime.isInTimeRange(df.parse("14:00:00"), 13, 30, 13, 59));
+        assertTrue(PacUtilsDateTime.isInTimeRange(df.parse("14:00:00"), 13, 30, 14, 0));
+        assertTrue(PacUtilsDateTime.isInTimeRange(df.parse("14:00:20"), 13, 30, 23, 14, 0, 21));
+        assertTrue(PacUtilsDateTime.isInTimeRange(df.parse("14:00:20"), 13, 30, 23, 14, 0, 20));
+        assertFalse(PacUtilsDateTime.isInTimeRange(df.parse("14:00:20"), 13, 30, 23, 14, 0, 19));
+        assertFalse(PacUtilsDateTime.isInTimeRange(df.parse("14:00:20"), 13, 30, 23, 14, 0, 19, "GMT"));
+        
+        assertTrue(PacUtilsDateTime.isInTimeRange(df.parse("23:12:20"), 22, 4));
+        
+    }
+
+    /**
+     * Test of isInDateRange method, of class PacUtilsDateTime.
+     */
+    @Test
+    public void testIsInDateRange() throws Exception {
+        System.out.println("isInDateRange");
+
+        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+        assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-02-28"), "FEB"));
+        assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-02-28"), "MAR", "JUN"));
+        assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-02-28"), "DEC", "MAR"));
+        assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-12-28"), "DEC", "MAR"));
+        assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-03-28"), "DEC", "MAR"));
+        assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-11-28"), "DEC", "MAR"));
+        assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), "DEC", "MAR"));
+
+        assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 20, 30));
+        assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 28));
+        assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 27));
+        assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 27, 28));
+        assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 10, 12));
+
+        assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 2016));
+        assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 2017));
+        assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 2016, 2020));
+        assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 2012, 2015));
+        
+        assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-04-28"), 15, "FEB", 31, "MAR"));
+        assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-03-28"), 15, "FEB", 31, "MAR"));
+        assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-03-28"), 15, "NOV", 31, "JAN"));
+        assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-12-28"), 15, "NOV", 31, "JAN"));
+        assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-01-28"), 15, "NOV", 31, "JAN"));
+        assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-02-01"), 15, "NOV", 31, "JAN"));
+        assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-11-01"), 15, "NOV", 31, "JAN"));
+        assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-09-16"), 15, "NOV", 31, "JAN"));
+        
+        assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2016-11-01"), "NOV", 2016, "JAN", 2017));
+        assertTrue(PacUtilsDateTime.isInDateRange(df.parse("2017-01-26"), "NOV", 2016, "JAN", 2017));
+        assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2016-10-30"), "NOV", 2016, "JAN", 2017));
+        assertFalse(PacUtilsDateTime.isInDateRange(df.parse("2017-02-01"), "NOV", 2016, "JAN", 2017));
+    }
+
+
+    
+}
diff --git a/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacUtilsTest.java b/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacUtilsTest.java
new file mode 100644
index 0000000..26f9e8d
--- /dev/null
+++ b/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/PacUtilsTest.java
@@ -0,0 +1,150 @@
+/*
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.proxy.pac;
+
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class PacUtilsTest {
+    
+    public PacUtilsTest() {
+    }
+    
+    @BeforeClass
+    public static void setUpClass() {
+    }
+    
+    @AfterClass
+    public static void tearDownClass() {
+    }
+    
+    @Before
+    public void setUp() {
+    }
+    
+    @After
+    public void tearDown() {
+    }
+
+   
+    /**
+     * Test of toStrippedURLStr method, of class PacUtils.
+     */
+    @Test
+    public void testToStrippedURLStr() throws URISyntaxException {
+        System.out.println("toStrippedURLStr");
+        
+        URI uri = new URI("https://mary@somehost.apache.org:8081/path/to/something?x1=Christmas&user=unknown");
+        
+        String str = PacUtils.toStrippedURLStr(uri);
+        String expResult = "https://somehost.apache.org:8081/";
+        assertEquals(expResult, str);
+    }
+   
+
+    /**
+     * Test of toSemiColonListStr method, of class PacUtils.
+     */
+    @Test
+    public void testToSemiColonListStr_InetAddressArr() {
+        System.out.println("toSemiColonListStr");
+        InetAddress[] addresses = getInetAddressListArr(
+                new String[]{
+                    "192.168.1.34",
+                    "192.168.1.56"
+                });
+        String expResult = "192.168.1.34;192.168.1.56";
+        String result = PacUtils.toSemiColonListInetAddress(addresses);
+        assertEquals(expResult, result);
+
+        addresses = getInetAddressListArr(
+                new String[]{
+                    "192.168.1.34"
+                });
+        expResult = "192.168.1.34";
+        result = PacUtils.toSemiColonListInetAddress(addresses);
+        assertEquals(expResult, result);
+
+        addresses = getInetAddressListArr(
+                new String[]{});
+        expResult = "";
+        result = PacUtils.toSemiColonListInetAddress(addresses);
+        assertEquals(expResult, result);
+
+    }
+
+   
+
+    /**
+     * Test of ipPrefixMatch method, of class PacUtils.
+     */
+    @Test
+    public void testIpPrefixMatch() {
+        System.out.println("ipPrefixMatch");
+        testIpPrefixMatch0("192.168.2.34",  "192.168.2.45/32", false);
+        testIpPrefixMatch0("192.168.2.122", "192.168.2.0/25", true);
+        testIpPrefixMatch0("192.168.2.129", "192.168.2.0/25", false);
+        testIpPrefixMatch0("192.168.2.129", "192.168.0.0/16", true);
+        testIpPrefixMatch0("192.169.2.129", "192.168.0.0/16", false);
+        
+        testIpPrefixMatch0("192.169.2.129", "3ffe:8311:ffff/48", false);
+        testIpPrefixMatch0("192.169.2.129", "3ffe:8311:ffff/24", false);
+        testIpPrefixMatch0("3ffe:8311:ffff:1a2b::", "3ffe:8311:ffff/24", true);
+        
+    }
+    
+    private void testIpPrefixMatch0(String address, String ipPrefix, boolean expResult) {
+        try {
+            InetAddress ipAddress = InetAddress.getByName(address);
+            if (PacUtils.ipPrefixMatch(ipAddress, ipPrefix) != expResult) {
+                fail(" For address=" + address + ", ipPrefix=" + ipPrefix + " the expected result was " + expResult);
+            }
+        } catch (UnknownHostException ex) {
+            fail("Error in test case : " + ex.getMessage() );
+        }
+    }
+    
+    
+    private InetAddress[] getInetAddressListArr(String[] ipAddresses) {
+        List<InetAddress> list = getInetAddressList(ipAddresses);
+        return list.toArray(new InetAddress[list.size()]);
+    }
+    
+    private List<InetAddress> getInetAddressList(String[] ipAddresses) {
+        List<InetAddress> list = new ArrayList<>();
+        for(String s : ipAddresses) {
+            try {
+                list.add(InetAddress.getByName(s));
+            } catch (UnknownHostException ex) {
+                fail("Error in test case " + ex.getMessage() );
+            }
+        }
+        return list;
+    }
+    
+}
diff --git a/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/impl/PacHelperMethodsImplTest.java b/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/impl/PacHelperMethodsImplTest.java
new file mode 100644
index 0000000..806dea9
--- /dev/null
+++ b/core.network/test/unit/src/org/netbeans/core/network/proxy/pac/impl/PacHelperMethodsImplTest.java
@@ -0,0 +1,333 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.proxy.pac.impl;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.List;
+import org.junit.After;
+import org.junit.AfterClass;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.netbeans.core.network.utils.FakeDns;
+
+/**
+ *
+ */
+public class PacHelperMethodsImplTest {
+
+    private FakeDns fakeDns;
+    private InetAddress ipv4Addr;
+    private InetAddress ipv6Addr;
+    private final NbPacHelperMethods helpers = new NbPacHelperMethods();
+    private final static List<String> WEEKDAY_NAMES = Collections.unmodifiableList(
+            Arrays.asList("SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"));
+
+    private final static List<String> MONTH_NAMES = Collections.unmodifiableList(
+            Arrays.asList("JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"));
+
+    public PacHelperMethodsImplTest() {
+
+    }
+
+    @BeforeClass
+    public static void setUpClass() {
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+    }
+
+    @Before
+    public void setUp() throws UnknownHostException {
+        fakeDns = new FakeDns();
+        fakeDns.install(true);
+        ipv4Addr = InetAddress.getByName("172.217.17.36");    // just some random address
+        ipv6Addr = InetAddress.getByName("2a00:1450:400e:804::2004"); // just some random address
+    }
+
+    @After
+    public void tearDown() {
+        fakeDns.unInstall();
+    }
+
+    /**
+     * Test of isPlainHostName method, of class helpers.
+     */
+    @org.junit.Test
+    public void testIsPlainHostName() {
+        assertTrue(helpers.isPlainHostName("somehost"));
+        assertFalse(helpers.isPlainHostName("somehost.dom1.com"));
+    }
+
+    /**
+     * Test of dnsDomainIs method, of class helpers.
+     */
+    @org.junit.Test
+    public void testDnsDomainIs() {
+        assertTrue(helpers.dnsDomainIs("www.netscape.com", ".netscape.com"));
+        assertTrue(helpers.dnsDomainIs("www.netscape.com", "netscape.com"));
+        assertFalse(helpers.dnsDomainIs("www.netscape.com", ".com"));
+        assertFalse(helpers.dnsDomainIs("www.netscape.com", "somethingelse.com"));
+        assertFalse(helpers.dnsDomainIs("www.netscape.com", ""));
+        assertFalse(helpers.dnsDomainIs("www.netscape.com", null));
+    }
+
+    /**
+     * Test of localHostOrDomainIs method, of class helpers.
+     */
+    @org.junit.Test
+    public void testLocalHostOrDomainIs() {
+        assertTrue(helpers.localHostOrDomainIs("www.netscape.com", "www.netscape.com"));
+        assertTrue(helpers.localHostOrDomainIs("www", "www.netscape.com"));
+        assertFalse(helpers.localHostOrDomainIs("www.netscape.com", "www.netscape.com2"));
+        assertFalse(helpers.localHostOrDomainIs("www1.netscape.com", "www2.netscape.com"));
+        assertFalse(helpers.localHostOrDomainIs("www1", "www2.netscape.com"));
+    }
+
+    /**
+     * Test of isResolvable method, of class helpers.
+     */
+    @org.junit.Test
+    public void testIsResolvable() {
+        fakeDns.addForwardResolution("isresolvable1host", new InetAddress[]{ipv4Addr});
+        fakeDns.addForwardResolution("isresolvable2host", new InetAddress[]{ipv6Addr});
+
+        assertTrue(helpers.isResolvable("isresolvable1host"));
+        assertFalse(helpers.isResolvable("isresolvable2host"));   // method should never return IPv6 addr
+        assertFalse(helpers.isResolvable("foo-bar-does-not-exist"));
+    }
+
+    /**
+     * Test of dnsResolve method, of class helpers.
+     */
+    @org.junit.Test
+    public void testDnsResolve() {
+
+        fakeDns.addForwardResolution("dnsresolve1host", new InetAddress[]{ipv4Addr});
+        fakeDns.addForwardResolution("dnsresolve2host", new InetAddress[]{ipv6Addr});
+
+        assertEquals(ipv4Addr.getHostAddress(), helpers.dnsResolve("dnsresolve1host"));
+
+        assertNull(helpers.dnsResolve("dnsresolve2host"));  // should not be able to resolve IPv6 address
+
+    }
+
+    /**
+     * Test of myIpAddress method, of class helpers.
+     */
+    @org.junit.Test
+    public void testMyIpAddress() {
+        String x = helpers.myIpAddress();
+    }
+
+    /**
+     * Test of isInNet method, of class helpers.
+     */
+    @org.junit.Test
+    public void testIsInNet() throws UnknownHostException {
+        fakeDns.addForwardResolution("isinnet1host", new InetAddress[]{InetAddress.getByName("127.0.0.1")});
+        assertTrue(helpers.isInNet("isinnet1host", "127.0.0.1", "255.255.255.255"));
+
+        fakeDns.addForwardResolution("isinnet2host", new InetAddress[]{InetAddress.getByName("::1"), InetAddress.getByName("127.0.0.1")});
+        assertTrue(helpers.isInNet("isinnet2host", "127.0.0.1", "255.255.255.255"));
+
+        fakeDns.addForwardResolution("isinnet3host", new InetAddress[]{InetAddress.getByName("::1"), InetAddress.getByName("127.0.0.99")});
+        assertFalse(helpers.isInNet("isinnet3host", "127.0.0.1", "255.255.255.255"));
+
+        fakeDns.addForwardResolution("isinnet4host", new InetAddress[]{InetAddress.getByName("::1")});
+        assertFalse(helpers.isInNet("isinnet4host", "127.0.0.1", "255.255.255.255"));
+
+        fakeDns.addForwardResolution("google-public-dns-a.google.com", new InetAddress[]{InetAddress.getByName("8.8.8.8")});
+        assertTrue(helpers.isInNet("google-public-dns-a.google.com", "8.8.8.8", "255.255.255.255"));
+
+        assertFalse(helpers.isInNet("192.168.1.3", "192.168.1.1", "255.255.255.255"));
+        assertTrue(helpers.isInNet("192.168.1.3", "192.168.1.1", "255.255.255.0"));
+        assertTrue(helpers.isInNet("192.168.1.1", "192.168.3.1", "255.255.0.255"));
+        assertTrue(helpers.isInNet("10.10.10.10", "12.12.12.12", "0.0.0.0"));
+        assertFalse(helpers.isInNet("10.10.10.10", "12.12.12.12", "0.0.255.0"));
+    }
+
+    /**
+     * Test of isInNetEx method, of class helpers.
+     */
+    @org.junit.Test
+    public void testIsInNetEx() throws UnknownHostException {
+
+        assertTrue(helpers.isInNetEx("198.95.249.79", "198.95.249.79/32"));
+        assertFalse(helpers.isInNetEx("198.95.249.78", "198.95.249.79/32"));
+        
+        assertTrue(helpers.isInNetEx("198.95.33.22", "198.95.0.0/16"));
+        assertFalse(helpers.isInNetEx("198.96.33.22", "198.95.0.0/16"));
+        assertTrue(helpers.isInNetEx("198.96.33.22", "198.95.0.0/16;198.96.0.0/16"));
+        
+        fakeDns.addForwardResolution("isinnetex1host", new InetAddress[]{InetAddress.getByName("2a00:1450:400e:804::2004")});
+        assertTrue(helpers.isInNetEx("isinnetex1host", "2a00:1450:400e/48"));
+        assertFalse(helpers.isInNetEx("isinnetex1host", "198.95.0.0/16"));
+        
+    }
+
+    /**
+     * Test of dnsDomainLevels method, of class helpers.
+     */
+    @org.junit.Test
+    public void testDnsDomainLevels() {
+        assertEquals(2, helpers.dnsDomainLevels("www.netscape.com"));
+        assertEquals(0, helpers.dnsDomainLevels("www"));
+        assertEquals(1, helpers.dnsDomainLevels("www."));  // somewhat undefined from Netscape spec what this should return, probably a corner case
+    }
+
+    /**
+     * Test of shExpMatch method, of class helpers.
+     */
+    @org.junit.Test
+    public void testShExpMatch() {
+        assertTrue(helpers.shExpMatch("www.netscape.com", "*netscape*"));
+        assertTrue(helpers.shExpMatch("www.netscape.com", "*net*"));
+        assertFalse(helpers.shExpMatch("www.netscape.com", "*google*"));
+        assertTrue(helpers.shExpMatch("www.netscape.com", "www*"));
+    }
+
+    /**
+     * Test of weekdayRange method, of class helpers.
+     */
+    @org.junit.Test
+    public void testWeekdayRange() {
+        assertTrue(helpers.weekdayRange("MON", "SUN", "GMT"));
+        assertTrue(helpers.weekdayRange("MON", "SUN", null));
+
+        Calendar now = Calendar.getInstance();
+        int weekdayNo = now.get(Calendar.DAY_OF_WEEK);  // 1-7  (1=Sunday, 7=Monday)
+        int weekdayNoBefore = weekdayNo - 1;
+        if (weekdayNoBefore == 0) {
+            weekdayNoBefore = 7;
+        }
+        int weekdayNoAfter = weekdayNo + 1;
+        if (weekdayNoAfter == 8) {
+            weekdayNoAfter = 1;
+        }
+        String weekdayNow = WEEKDAY_NAMES.get(weekdayNo - 1);
+        String weekdayBefore = WEEKDAY_NAMES.get(weekdayNoBefore - 1);
+        String weekdayAfter = WEEKDAY_NAMES.get(weekdayNoAfter - 1);
+        assertTrue(helpers.weekdayRange(weekdayNow, null, null));
+        assertTrue(helpers.weekdayRange(weekdayBefore, weekdayAfter, null));
+    }
+
+    /**
+     * Test of dateRange method, of class helpers.
+     */
+    @org.junit.Test
+    public void testDateRange() {
+        System.out.println("dateRange");
+
+        Calendar now = Calendar.getInstance();
+
+        int nowDay = now.get(Calendar.DAY_OF_MONTH);
+        String nowMonth = MONTH_NAMES.get(now.get(Calendar.MONTH));
+        int nowYear = now.get(Calendar.YEAR);
+
+        assertTrue(helpers.dateRange(nowDay));
+        assertFalse(helpers.dateRange(nowDay + 1));
+        assertTrue(helpers.dateRange(nowMonth));
+        assertTrue(helpers.dateRange(nowYear));
+        assertFalse(helpers.dateRange(nowYear + 1));
+        assertTrue(helpers.dateRange(nowDay, nowMonth, nowDay, nowMonth));
+        assertTrue(helpers.dateRange(nowMonth, nowYear, nowMonth, nowYear));
+        assertFalse(helpers.dateRange(nowMonth, nowYear + 1, nowMonth, nowYear + 1));
+    }
+
+    /**
+     * Test of timeRange method, of class helpers.
+     */
+    @org.junit.Test
+    public void testTimeRange() {
+        Calendar now = Calendar.getInstance();
+
+        final int nowHour = now.get(Calendar.HOUR_OF_DAY);
+        final int nowMinute = now.get(Calendar.MINUTE);
+        final int nowSecond = now.get(Calendar.SECOND);  // not used
+        int nowMinuteNext = (nowMinute == 59) ? 0 : nowMinute + 1;
+        int nowHourNext = (nowMinuteNext == 0) ? ((nowHour == 23) ? 0 : nowHour + 1) : nowHour;
+        assertTrue(helpers.timeRange(nowHour));
+        assertTrue(helpers.timeRange(nowHour, nowHour));
+        assertTrue(helpers.timeRange(nowHour, nowMinute, nowHourNext, nowMinuteNext));
+        assertFalse(helpers.timeRange(23, 31, 12, 23, 31, 5));     // second timestamp before first - should give false result
+        assertFalse(helpers.timeRange(23, 31, 12, 23, 31, 5, "GMT"));     // second timestamp before first - should give false result
+        assertFalse(helpers.timeRange(23, 31, 12, 23, 31, 12, "GMT"));     // test will fail if we happen to run test at exactly 23:31:12 , unlikely!
+
+        // We don't dare test something with nowSecond as there's a real
+        // risk that two calendars (the one used in the actual code vs the one used in this
+        // test class) are not on the same second .. and the test will then give incorrect result.
+    }
+
+    /**
+     * Test of isResolvableEx method, of class helpers.
+     */
+    @org.junit.Test
+    public void testIsResolvableEx() {
+        fakeDns.addForwardResolution("isresolvableex1host", new InetAddress[]{ipv4Addr});
+        fakeDns.addForwardResolution("isresolvableex2host", new InetAddress[]{ipv6Addr});
+
+        assertTrue(helpers.isResolvableEx("isresolvableex1host"));
+        assertTrue(helpers.isResolvableEx("isresolvableex2host"));
+        assertFalse(helpers.isResolvableEx("foo-bar-does-not-exist"));
+    }
+
+    /**
+     * Test of dnsResolveEx method, of class helpers.
+     */
+    @org.junit.Test
+    public void testDnsResolveEx() {
+
+        fakeDns.addForwardResolution("dnsresolveex1host", new InetAddress[]{ipv4Addr});
+        fakeDns.addForwardResolution("dnsresolveex2host", new InetAddress[]{ipv6Addr});
+
+        assertEquals(ipv4Addr.getHostAddress(), helpers.dnsResolveEx("dnsresolveex1host"));
+        assertEquals(ipv6Addr.getHostAddress(), helpers.dnsResolveEx("dnsresolveex2host"));
+    }
+
+    /**
+     * Test of sortIpAddressList method, of class helpers.
+     */
+    @org.junit.Test
+    public void testSortIpAddressList() {
+
+        String input
+                = "10.2.3.9"
+                + ";" + "2001:4898:28:3:201:2ff:feea:fc14"
+                + ";" + "::1"
+                + ";" + "127.0.0.1"
+                + ";" + "::9";
+
+        String expected
+                = "::1"
+                + ";" + "::9"
+                + ";" + "2001:4898:28:3:201:2ff:feea:fc14"
+                + ";" + "10.2.3.9"
+                + ";" + "127.0.0.1";
+        assertEquals(expected, helpers.sortIpAddressList(input));
+    }
+}
diff --git a/core.network/test/unit/src/org/netbeans/core/network/utils/FakeDns.java b/core.network/test/unit/src/org/netbeans/core/network/utils/FakeDns.java
new file mode 100644
index 0000000..8631445
--- /dev/null
+++ b/core.network/test/unit/src/org/netbeans/core/network/utils/FakeDns.java
@@ -0,0 +1,216 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.core.network.utils;
+
+import java.lang.reflect.Field;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import sun.net.spi.nameservice.NameService;
+
+/**
+ * A simple In-Memory implementation of a Name Service for Java. This
+ * is useful for unit tests.
+ * 
+ * <p>
+ * Uses dirty tricks (reflection) and classes from sun.* package. For unit
+ * testing this is acceptable. For anything else: no!
+ * 
+ * <p>
+ * Credit: The code is inspired by similar approach in Apache Kudo project. 
+ * 
+ * @author lbruun
+ */
+public class FakeDns {
+
+
+    private final Map<String, InetAddress[]> forwardResolutions = new ConcurrentHashMap<>();
+    private final Map<InetAddress, String> reverseResolutions = new ConcurrentHashMap<>();
+    private final List<NameService> orgNameServices = new ArrayList<>();   
+    private volatile boolean installed = false;
+    private final AtomicLong delayAnsweringByMs = new AtomicLong(0);
+
+    public FakeDns() {
+    }
+
+
+    /**
+     * Adds a hostname-to-address(es) mapping. This will also add the
+     * corresponding reverse mapping(s).
+     * @param hostname
+     * @param ipAdresses
+     */
+    public void addForwardResolution(String hostname, InetAddress[] ipAdresses) {
+        forwardResolutions.put(hostname, ipAdresses);
+        for(InetAddress a : ipAdresses) {
+            reverseResolutions.put(a, hostname);
+        }
+    }
+
+    /**
+     * Adds an address-to-hostname mapping. The 
+     * {@link #addForwardResolution(java.net.InetAddress, java.lang.String) addReverseResolution()}
+     * method will automatically add reverse mappings too, so this method is only
+     * necessary if you want to add a reverse mapping for which no forward mapping
+     * exist, i.e. a "standalone" reverse mapping.
+     * 
+     * @param ipAddress
+     * @param hostname
+     */
+    public void addReverseResolution(InetAddress ipAddress, String hostname) {
+        reverseResolutions.put(ipAddress, hostname);
+    }
+
+    /**
+     * Install the fake DNS resolver into the Java runtime.
+     * 
+     * <p>
+     * In Java, name services are chained. So if one name service doesn't 
+     * respond (or responds with {@code UnknownHostException}) then the next
+     * name service in the chain will be queried. If you want to do negative
+     * tests, i.e. "this should give me UnknownHostException", then you'll have
+     * to remove other name services too, so that only the FakeDns name service
+     * is installed. You do this by setting {@code removeOthers} to {@code true}.
+     * 
+     * @param removeOthers if other name services should be removed
+     */
+    public synchronized void install(boolean removeOthers) {
+        if (installed) {
+            return;
+        }
+        List<NameService> nameServices = getKnownNameServices();
+        if (nameServices != null) {
+
+            if (removeOthers) {
+                orgNameServices.clear();
+                orgNameServices.addAll(nameServices);
+                nameServices.clear();
+            }
+            // Install ourselves in position 0, ahead of everyone else
+            nameServices.add(0, new NameServiceInMemory());
+
+            installed = true;
+        }
+    }
+
+    /**
+     * Puts Java's internal name service chain back to its original state.
+     * Clears any forward or reverse mappings that has been put into the
+     * FakeDns.
+     */
+    public synchronized void unInstall() {
+        if (!installed) {
+            return;
+        }
+        List<NameService> nameServices = getKnownNameServices();
+        if (nameServices != null) {
+
+            NameService ns = nameServices.get(0);
+            if (ns != null) {
+                if (ns instanceof NameServiceInMemory) {
+                    nameServices.remove(0);
+                    installed = false;
+                }
+            }
+            
+            if (!orgNameServices.isEmpty()) {
+                nameServices.addAll(orgNameServices);
+                orgNameServices.clear();
+            }
+        }
+        forwardResolutions.clear();
+        reverseResolutions.clear();
+    }
+
+    /**
+     * Sets a delay on answering, thereby simulating a DNS server which
+     * is slow to respond.
+     * 
+     * <p>
+     * Set to 0 (zero) to remove the delay again.
+     * 
+     * <p>
+     * Beware that Java also has a name service cache, so our FakeDns name
+     * service may not be queried at all as a we expect. Therefore, when 
+     * testing for a delayed response it is best to it on a newly 
+     * inserted mapping.
+     * 
+     * @param msWait milliseconds to wait before answering to calls
+     */
+    public void delayAnsweringBy(long msWait ) {
+        delayAnsweringByMs.set(msWait);
+    }
+    
+    private List<NameService> getKnownNameServices() {
+        try {
+            Field field = InetAddress.class.getDeclaredField("nameServices");
+            field.setAccessible(true);
+            return (List<NameService>) field.get(null);
+        } catch (IllegalAccessException | IllegalArgumentException | NoSuchFieldException | SecurityException e) {
+            //throw Throwables.propagate(e);
+        }
+        return null;
+    }
+
+    private class NameServiceInMemory implements NameService {
+
+        @Override
+        public InetAddress[] lookupAllHostAddr(String host)
+                throws UnknownHostException {
+            sleep();
+            InetAddress[] inetAddresses = forwardResolutions.get(host);
+            if (inetAddresses != null) {
+                return inetAddresses;
+            }
+
+            // Since Java 7, JDK chains to the next name servicer provider 
+            // automatically, so when we throw UnknownHostException, the 
+            // resolution will not actually stop (provided there are more name 
+            // service implementations in the chain), instead it will continue
+            // to the next in the chain. This means that our FakeDns cannot 
+            // be used to test cases where we want to test for a negative
+            throw new UnknownHostException();
+        }
+
+        @Override
+        public String getHostByAddr(byte[] addr) throws UnknownHostException {
+            sleep();
+            String hostname = reverseResolutions.get(InetAddress.getByAddress(addr));
+            if (hostname != null) {
+                return hostname;
+            }
+
+            throw new UnknownHostException();
+        }
+        
+        private void sleep() {
+            final long delay = delayAnsweringByMs.get();
+            if (delay != 0) {
+                try {
+                    Thread.sleep(delay);
+                } catch (InterruptedException ex) {
+                }
+            }
+        }
+    }
+}
diff --git a/core.network/test/unit/src/org/netbeans/core/network/utils/IpAddressUtilsTest.java b/core.network/test/unit/src/org/netbeans/core/network/utils/IpAddressUtilsTest.java
new file mode 100644
index 0000000..47b6f4f
--- /dev/null
+++ b/core.network/test/unit/src/org/netbeans/core/network/utils/IpAddressUtilsTest.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.utils;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.concurrent.TimeoutException;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class IpAddressUtilsTest {
+
+    private FakeDns fakeDns;
+    private InetAddress ipv4Addr;
+    private InetAddress ipv6Addr;
+
+    public IpAddressUtilsTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() {
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+    }
+
+    @Before
+    public void setUp() throws UnknownHostException {
+        fakeDns = new FakeDns();
+        fakeDns.install(true);
+
+        ipv4Addr = InetAddress.getByName("172.217.17.36"); // just some random address
+        ipv6Addr = InetAddress.getByName("2a00:1450:400e:804::2004"); // just some random address
+    }
+
+    @After
+    public void tearDown() {
+        fakeDns.unInstall();
+    }
+
+    /**
+     * Test of nameResolve method, of class IpAddressUtils.
+     */
+    @Test
+    public void testNameResolveArr() throws Exception {
+        System.out.println("nameResolve");
+
+        InetAddress[] addrInput = new InetAddress[]{ipv4Addr, ipv6Addr};
+        fakeDns.addForwardResolution("test1", addrInput);
+        InetAddress[] addresses2 = IpAddressUtils.nameResolveArr("test1", 1000, IpAddressUtils.IpTypePreference.ANY_IPV4_PREF);
+        assertArrayEquals(addrInput, addresses2);
+        InetAddress[] addresses3 = IpAddressUtils.nameResolveArr("test1", 1000, IpAddressUtils.IpTypePreference.ANY_IPV6_PREF);
+        assertArrayEquals(new InetAddress[]{ipv6Addr, ipv4Addr}, addresses3);
+        InetAddress[] addresses4 = IpAddressUtils.nameResolveArr("test1", 1000, IpAddressUtils.IpTypePreference.IPV4_ONLY);
+        assertArrayEquals(new InetAddress[]{ipv4Addr}, addresses4);
+        InetAddress[] addresses5 = IpAddressUtils.nameResolveArr("test1", 1000, IpAddressUtils.IpTypePreference.IPV6_ONLY);
+        assertArrayEquals(new InetAddress[]{ipv6Addr}, addresses5);
+        
+        
+        fakeDns.delayAnsweringBy(4000);
+        fakeDns.addForwardResolution("test2", addrInput);
+        try {
+            InetAddress[] addresses6 = IpAddressUtils.nameResolveArr("test2", 1000, IpAddressUtils.IpTypePreference.ANY_IPV4_PREF);
+            fail("This should have timed out");
+        } catch (TimeoutException ex) {
+        }
+        
+    }
+
+    /**
+     * Test of isValidIpv4Address method, of class IpAddressUtils.
+     */
+    @Test
+    public void testIsValidIP4Address() {
+        System.out.println("isValidIP4Address");
+        assertTrue(IpAddressUtils.isValidIpv4Address("192.168.1.33"));
+        assertFalse(IpAddressUtils.isValidIpv4Address(".3245.23"));
+        assertFalse(IpAddressUtils.isValidIpv4Address("192.923.1.33"));
+        assertFalse(IpAddressUtils.isValidIpv4Address("192.168.1."));
+    }
+
+    /**
+     * Test of looksLikeIpv4Literal method, of class IpAddressUtils.
+     */
+    @Test
+    public void testLooksLikeIpv4Literal() {
+        System.out.println("looksLikeIpv4Literal");
+        assertTrue(IpAddressUtils.looksLikeIpv4Literal("192.168.1.33"));
+        assertTrue(IpAddressUtils.looksLikeIpv4Literal("192.168"));
+        assertFalse(IpAddressUtils.looksLikeIpv4Literal("1923.43.23.2"));
+        assertFalse(IpAddressUtils.looksLikeIpv4Literal("192"));
+        assertFalse(IpAddressUtils.looksLikeIpv4Literal("192.a68"));
+        assertFalse(IpAddressUtils.looksLikeIpv4Literal("abc."));
+        assertFalse(IpAddressUtils.looksLikeIpv4Literal(".abc"));
+        assertFalse(IpAddressUtils.looksLikeIpv4Literal("abc.x"));
+        
+        // ::8.144.52.38  is a valid IPv6 address, but is not an IPv4 address
+        assertFalse(IpAddressUtils.looksLikeIpv4Literal("::8.144.52.38"));
+    }
+
+    /**
+     * Test of looksLikeIpv6Literal method, of class IpAddressUtils.
+     */
+    @Test
+    public void testLooksLikeIpv6Literal() {
+        System.out.println("looksLikeIpv6Literal");
+        assertFalse(IpAddressUtils.looksLikeIpv6Literal("192.168.1.33"));
+        assertFalse(IpAddressUtils.looksLikeIpv6Literal("3d"));
+        
+        assertTrue(IpAddressUtils.looksLikeIpv6Literal("::1"));
+        assertTrue(IpAddressUtils.looksLikeIpv6Literal("::1:3d"));
+        assertTrue(IpAddressUtils.looksLikeIpv6Literal("1080:46A2:6F3A:9D1C:F2AB:8D12:200C:417A"));
+        assertFalse(IpAddressUtils.looksLikeIpv6Literal("1080:46A2:6F3A:9D1C:F2AB:8D12:200C:417A:"));
+        assertFalse(IpAddressUtils.looksLikeIpv6Literal("1080:46A2:6F3A:9D1C:F2AB:8D12:200C:417A:3A"));
+        
+        // The values below are certainly not valid IPv6 addresses 
+        // but they kinda look like one and they certainly cannot be
+        // hostnames. This is enough for the method to return true.
+        assertTrue(IpAddressUtils.looksLikeIpv6Literal(":foobarfoorbar"));
+        assertTrue(IpAddressUtils.looksLikeIpv6Literal("foobarfoorbar:"));
+    }
+    
+    /**
+     * Test of removeDomain method, of class IpAddressUtils.
+     */
+    @Test
+    public void testRemoveDomain() {
+        System.out.println("removeDomain");
+
+        String input;
+
+        input = ".hardlymakessense";
+        assertEquals("", IpAddressUtils.removeDomain(input));
+
+        input = "chicago.internal.net";
+        assertEquals("chicago", IpAddressUtils.removeDomain(input));
+
+        input = "chicago";
+        assertEquals(input, IpAddressUtils.removeDomain(input));
+        
+        input = "123.123";
+        assertEquals(input, IpAddressUtils.removeDomain(input));
+        
+        input = "3D::123.123";
+        assertEquals(input, IpAddressUtils.removeDomain(input));
+    }
+
+}
diff --git a/core.network/test/unit/src/org/netbeans/core/network/utils/LocalAddressUtilsTest.java b/core.network/test/unit/src/org/netbeans/core/network/utils/LocalAddressUtilsTest.java
new file mode 100644
index 0000000..c71c5a9
--- /dev/null
+++ b/core.network/test/unit/src/org/netbeans/core/network/utils/LocalAddressUtilsTest.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.utils;
+
+import java.net.InetAddress;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ *
+ * @author lbruun
+ */
+public class LocalAddressUtilsTest {
+    
+    public LocalAddressUtilsTest() {
+    }
+    
+    @BeforeClass
+    public static void setUpClass() {
+    }
+    
+    @AfterClass
+    public static void tearDownClass() {
+    }
+    
+    @Before
+    public void setUp() {
+    }
+    
+    @After
+    public void tearDown() {
+    }
+
+   
+
+    /**
+     * Test of getLocalHostAddr method, of class LocalAddressUtils.
+     */
+    @Test
+    public void testGetLocalHostAddr() {
+        System.out.println("getLocalHostAddr");
+    }
+
+    /**
+     * Test of getLoopbackAddress method, of class LocalAddressUtils.
+     */
+    @Test
+    public void testGetLoopbackAddress() {
+        System.out.println("getLoopbackAddress");
+    }
+
+    /**
+     * Test of refreshLocalNetworkInterfaceAddr method, of class LocalAddressUtils.
+     */
+    @Test
+    public void testRefreshLocalNetworkInterfaceAddr() {
+        System.out.println("refreshLocalNetworkInterfaceAddr");
+    }
+
+    /**
+     * Test of refreshLocalHostAddr method, of class LocalAddressUtils.
+     */
+    @Test
+    public void testRefreshLocalHostAddr() {
+        System.out.println("refreshLocalHostAddr");
+    }
+    
+}
diff --git a/core.network/test/unit/src/org/netbeans/core/network/utils/SimpleCacheTest.java b/core.network/test/unit/src/org/netbeans/core/network/utils/SimpleCacheTest.java
new file mode 100644
index 0000000..b40ca2f
--- /dev/null
+++ b/core.network/test/unit/src/org/netbeans/core/network/utils/SimpleCacheTest.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.netbeans.core.network.utils;
+
+import java.util.UUID;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author lbruun
+ */
+public class SimpleCacheTest {
+    
+    private final SimpleObjCache<String,String> cache;
+    public SimpleCacheTest() {
+        cache = new SimpleObjCache<>(10);
+    }
+    
+    
+
+    @Test
+    public void testCache() {
+        System.out.println("testCache");
+        
+        cache.put("01", UUID.randomUUID().toString());
+        cache.put("02", UUID.randomUUID().toString());
+        cache.put("03", UUID.randomUUID().toString());
+        cache.put("04", UUID.randomUUID().toString());
+        cache.put("05", UUID.randomUUID().toString());
+        cache.put("06", UUID.randomUUID().toString());
+        cache.put("07", UUID.randomUUID().toString());
+        cache.put("08", UUID.randomUUID().toString());
+        cache.put("09", UUID.randomUUID().toString());
+        cache.put("10", UUID.randomUUID().toString());
+ 
+        shortWait();
+        assertNotNull(cache.get("01"));   // reset last usage timestamp for this element
+
+        // We've reached max capacity. Elements will start
+        // to get evicted.
+
+        shortWait();
+        cache.put("11", UUID.randomUUID().toString());
+        assertTrue(cache.getCacheSize() == 10);
+        
+        
+        assertNull(cache.get("02"));  // "02" will have been evicted by now
+
+        shortWait();
+        cache.put("12", UUID.randomUUID().toString());
+        assertTrue(cache.getCacheSize() == 10);
+        
+        assertNull(cache.get("03"));  // "03" will have been evicted by now
+        assertNotNull(cache.get("01")); // "01" is still there.
+        
+    }
+
+    @Test
+    public void testCacheConcurrency() {
+        System.out.println("testCache - concurrency ");
+        
+        // See if we can provoke ConcurrentModificationException.
+        // That should not be the case, because of use of ConcurrentHashMap.
+        
+        // However, the use of ConcurrentHashMap is not immune to
+        // NoSuchElementException being thrown from the Optional#get() in the
+        // findEvictionCandidate() method.
+
+        SimpleObjCache<Integer, String> intCache = new SimpleObjCache<>(3);    // very low capacity cache so that we get a lot of contention
+        
+        int noOfThreads = 15;
+        CacheTesterThread[] threads = new CacheTesterThread[noOfThreads];
+        // Prepare threads
+        for(int i = 0; i<noOfThreads; i++) {
+            threads[i] = new CacheTesterThread(intCache);
+        }
+        // Start threads
+        for(int i = 0; i<noOfThreads; i++) {
+            threads[i].start();
+        }
+
+    }
+    
+        
+    
+    private void shortWait() {
+        // The cache only tracks usage of elements down to the millisecond. 
+        // So, potentially, two elements can have exactly the same timestamp. 
+        // For this reason, it is necessary to wait a bit in order to 
+        // accurately predict which element will be evicted, thus only needed
+        // if we need to predict which element will be evicted.
+        try {
+            Thread.sleep(50);
+        } catch (InterruptedException ex) {
+        }
+    }
+    
+    
+    private static class CacheTesterThread extends Thread {
+
+        private final SimpleObjCache<Integer, String> cache;
+
+        public CacheTesterThread(SimpleObjCache cache) {
+            this.cache = cache;
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < 100000; i++) {
+                // Do a bit of both writing and reading
+                cache.put(i % 10, "foo");
+                String val = cache.get(i);
+                cache.put((i % 10) + 1, val);
+            }
+        }
+    }
+}
diff --git a/libs.jna.platform/manifest.mf b/libs.jna.platform/manifest.mf
index c32f2f7..e7ad862 100644
--- a/libs.jna.platform/manifest.mf
+++ b/libs.jna.platform/manifest.mf
@@ -1,4 +1,4 @@
 Manifest-Version: 1.0
 OpenIDE-Module: org.netbeans.libs.jna.platform/1
 OpenIDE-Module-Localizing-Bundle: org/netbeans/libs/jna/platform/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.15
+OpenIDE-Module-Specification-Version: 1.16
diff --git a/libs.jna.platform/nbproject/project.xml b/libs.jna.platform/nbproject/project.xml
index 0e3792e..bc04a1f 100644
--- a/libs.jna.platform/nbproject/project.xml
+++ b/libs.jna.platform/nbproject/project.xml
@@ -35,6 +35,7 @@
             </module-dependencies>
             <friend-packages>
                 <friend>org.netbeans.core.nativeaccess</friend>
+                <friend>org.netbeans.core.network</friend>
                 <friend>org.netbeans.libs.jsch.agentproxy</friend>
                 <package>com.sun.jna.platform</package>
                 <package>com.sun.jna.platform.dnd</package>
diff --git a/nbbuild/build.properties b/nbbuild/build.properties
index a0386dc..5701f4c 100644
--- a/nbbuild/build.properties
+++ b/nbbuild/build.properties
@@ -188,6 +188,7 @@ config.javadoc.devel=\
 config.javadoc.friend=\
     o.n.bootstrap,\
     o.n.core,\
+    core.network,\
     code.analysis,\
     core.startup,\
     classfile,\
diff --git a/nbbuild/javadoctools/links.xml b/nbbuild/javadoctools/links.xml
index 6cfb654..db87cae 100644
--- a/nbbuild/javadoctools/links.xml
+++ b/nbbuild/javadoctools/links.xml
@@ -229,3 +229,4 @@
 <link href="${javadoc.docs.org-netbeans-modules-xml-text}" offline="true" packagelistloc="${netbeans.javadoc.dir}/org-netbeans-modules-xml-text"/>
 <link href="${javadoc.docs.org-netbeans-modules-xml-lexer}" offline="true" packagelistloc="${netbeans.javadoc.dir}/org-netbeans-modules-xml-lexer"/>
 <link href="${javadoc.docs.org-netbeans-modules-xml-text}" offline="true" packagelistloc="${netbeans.javadoc.dir}/org-netbeans-modules-xml-text"/>
+<link href="${javadoc.docs.org-netbeans-core-network}" offline="true" packagelistloc="${netbeans.javadoc.dir}\org-netbeans-core-network"/>
diff --git a/nbbuild/javadoctools/properties.xml b/nbbuild/javadoctools/properties.xml
index 11882c4..bd0edd8 100644
--- a/nbbuild/javadoctools/properties.xml
+++ b/nbbuild/javadoctools/properties.xml
@@ -227,3 +227,4 @@
 <property name="javadoc.docs.org-netbeans-modules-xml-text" value="${javadoc.web.root}/org-netbeans-modules-xml-text"/>
 <property name="javadoc.docs.org-netbeans-modules-xml-lexer" value="${javadoc.web.root}/org-netbeans-modules-xml-lexer"/>
 <property name="javadoc.docs.org-netbeans-modules-xml-text" value="${javadoc.web.root}/org-netbeans-modules-xml-text"/>
+<property name="javadoc.docs.org-netbeans-core-network" value="${javadoc.web.root}/org-netbeans-core-network"/>
diff --git a/nbbuild/javadoctools/replaces.xml b/nbbuild/javadoctools/replaces.xml
index b58fb8a..72e11a4 100644
--- a/nbbuild/javadoctools/replaces.xml
+++ b/nbbuild/javadoctools/replaces.xml
@@ -227,3 +227,4 @@
 <replacefilter token="@org-netbeans-modules-xml-text@" value="${javadoc.docs.org-netbeans-modules-xml-text}"/>
 <replacefilter token="@org-netbeans-modules-xml-lexer@" value="${javadoc.docs.org-netbeans-modules-xml-lexer}"/>
 <replacefilter token="@org-netbeans-modules-xml-text@" value="${javadoc.docs.org-netbeans-modules-xml-text}"/>
+<replacefilter token="@org-netbeans-core-network@" value="${javadoc.docs.org-netbeans-core-network}"/>

-- 
To stop receiving notification emails like this one, please contact
['"commits@netbeans.apache.org" <co...@netbeans.apache.org>'].