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

[GitHub] asfgit closed pull request #161: [NETBEANS-96] New PAC Script evaluation environment

asfgit closed pull request #161: [NETBEANS-96] New PAC Script evaluation environment
URL: https://github.com/apache/incubator-netbeans/pull/161
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/core.network/apichanges.xml b/core.network/apichanges.xml
new file mode 100644
index 000000000..c2e52a003
--- /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 000000000..a1d257fd6
--- /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 87372f1d5..000000000
--- 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 ac0e2db4c..000000000
--- 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 41db36ca5..c9b89a705 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 6e6e30139..a19bad12e 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 d89cca496..0fe2742bc 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 9c2bd8f5f..bee29a158 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 2a6db7025..008d80bc9 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 @@
 
     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 static synchronized ProxyAutoConfig get(String pacFile) {
         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 URI getPacURI() {
     }
 
     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 void run() {
                 }
             }
         }
-        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 @@ private static InputStream downloadPAC (URL pacURL) throws IOException {
         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 @@ private String normalizePAC(String pacURL) {
         }
         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 000000000..eb08624df
--- /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 000000000..f9cef13a9
--- /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 000000000..8531e9895
--- /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 000000000..6a4da3b1b
--- /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 000000000..d7b480b6f
--- /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 000000000..ad3d755f3
--- /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 000000000..d7a703f39
--- /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 000000000..047db8c63
--- /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 000000000..c7b8a2f58
--- /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 000000000..b96d17dd6
--- /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 000000000..782e1aa4b
--- /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 000000000..e08d2ce95
--- /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 000000000..c603ad087
--- /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 000000000..a84e81239
--- /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 000000000..04b2d4ded
--- /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 000000000..4346a26b9
--- /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 000000000..874f2d4bb
--- /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 000000000..c8bcfdde3
--- /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 000000000..585e0e4f0
--- /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 000000000..b5c69dbc7
--- /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 000000000..6e76439bc
--- /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 000000000..d9749442f
--- /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 000000000..50c3037d1
--- /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 000000000..5fa0a7b23
--- /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 000000000..0fa3713dc
--- /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 000000000..70f29fad4
--- /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 000000000..a7da2e62e
--- /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 000000000..a8e891ed3
--- /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 000000000..a3e017d08
--- /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 000000000..23b3b331b
--- /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 000000000..7b21e91e0
--- /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 000000000..09c523748
--- /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 000000000..f07919bfc
--- /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 000000000..94193689d
--- /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 000000000..81b2e4cd6
--- /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 000000000..338385003
--- /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 000000000..340bdefef
--- /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 000000000..c38be7a65
--- /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 000000000..fa4cb98e0
--- /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 000000000..dcb557e20
--- /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 e01044894..3310e6a7b 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 e01044894..3310e6a7b 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 000000000..fb4c654d8
--- /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 000000000..715e40876
--- /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 000000000..72a3ae6be
--- /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 000000000..d19f77993
--- /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 000000000..084863991
--- /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 728d2f154..2be4197ab 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 void testGetProxyAutoConfigWithLocalPAC() throws URISyntaxException {
             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 000000000..7d109a7ff
--- /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 000000000..3734f81a1
--- /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 000000000..26f9e8ddf
--- /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 000000000..806dea9bc
--- /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 000000000..8631445dc
--- /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 000000000..47b6f4f7a
--- /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 000000000..c71c5a96e
--- /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 000000000..b40ca2fb2
--- /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 c32f2f753..e7ad862f8 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 0e3792ee8..bc04a1fd1 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 a0386dc69..5701f4cfb 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 6cfb654b5..db87caeff 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 11882c4fb..bd0edd8e1 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 b58fb8ad9..72e11a4da 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}"/>


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services