synched w/ trunk docker_juds
authorPetr Hejl <phejl@netbeans.org>
Thu, 24 Mar 2016 15:34:03 +0100
branchdocker_juds
changeset 30713822b99cd1d4c7
parent 307081 56c607e03f37
parent 307137 69a41049166c
child 307139 5c14eb6c33bb
synched w/ trunk
docker.api/src/org/netbeans/modules/docker/api/DockerIntegration.java
docker.ui/src/org/netbeans/modules/docker/ui/node/EnhancedDockerContainer.java
docker.ui/src/org/netbeans/modules/docker/ui/node/EnhancedDockerInstance.java
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/docker.api/external/binaries-list	Thu Mar 24 15:34:03 2016 +0100
     1.3 @@ -0,0 +1,1 @@
     1.4 +C17B8FF394BF8C8C786D43710CE4C3995C803E82 juds-0.95.jar
     1.5 \ No newline at end of file
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/docker.api/external/juds-0.95-license.txt	Thu Mar 24 15:34:03 2016 +0100
     2.3 @@ -0,0 +1,507 @@
     2.4 +Name: juds
     2.5 +Version: 0.95
     2.6 +OSR: TBD
     2.7 +License: LGPL-2.1
     2.8 +Description: Java Unix Domain Sockets
     2.9 +Origin: https://github.com/mcfunley/juds
    2.10 +
    2.11 +            GNU LESSER GENERAL PUBLIC LICENSE
    2.12 +               Version 2.1, February 1999
    2.13 +
    2.14 + Copyright (C) 1991, 1999 Free Software Foundation, Inc.
    2.15 +     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    2.16 + Everyone is permitted to copy and distribute verbatim copies
    2.17 + of this license document, but changing it is not allowed.
    2.18 +
    2.19 +[This is the first released version of the Lesser GPL.  It also counts
    2.20 + as the successor of the GNU Library Public License, version 2, hence
    2.21 + the version number 2.1.]
    2.22 +
    2.23 +                Preamble
    2.24 +
    2.25 +  The licenses for most software are designed to take away your
    2.26 +freedom to share and change it.  By contrast, the GNU General Public
    2.27 +Licenses are intended to guarantee your freedom to share and change
    2.28 +free software--to make sure the software is free for all its users.
    2.29 +
    2.30 +  This license, the Lesser General Public License, applies to some
    2.31 +specially designated software packages--typically libraries--of the
    2.32 +Free Software Foundation and other authors who decide to use it.  You
    2.33 +can use it too, but we suggest you first think carefully about whether
    2.34 +this license or the ordinary General Public License is the better
    2.35 +strategy to use in any particular case, based on the explanations below.
    2.36 +
    2.37 +  When we speak of free software, we are referring to freedom of use,
    2.38 +not price.  Our General Public Licenses are designed to make sure that
    2.39 +you have the freedom to distribute copies of free software (and charge
    2.40 +for this service if you wish); that you receive source code or can get
    2.41 +it if you want it; that you can change the software and use pieces of
    2.42 +it in new free programs; and that you are informed that you can do
    2.43 +these things.
    2.44 +
    2.45 +  To protect your rights, we need to make restrictions that forbid
    2.46 +distributors to deny you these rights or to ask you to surrender these
    2.47 +rights.  These restrictions translate to certain responsibilities for
    2.48 +you if you distribute copies of the library or if you modify it.
    2.49 +
    2.50 +  For example, if you distribute copies of the library, whether gratis
    2.51 +or for a fee, you must give the recipients all the rights that we gave
    2.52 +you.  You must make sure that they, too, receive or can get the source
    2.53 +code.  If you link other code with the library, you must provide
    2.54 +complete object files to the recipients, so that they can relink them
    2.55 +with the library after making changes to the library and recompiling
    2.56 +it.  And you must show them these terms so they know their rights.
    2.57 +
    2.58 +  We protect your rights with a two-step method: (1) we copyright the
    2.59 +library, and (2) we offer you this license, which gives you legal
    2.60 +permission to copy, distribute and/or modify the library.
    2.61 +
    2.62 +  To protect each distributor, we want to make it very clear that
    2.63 +there is no warranty for the free library.  Also, if the library is
    2.64 +modified by someone else and passed on, the recipients should know
    2.65 +that what they have is not the original version, so that the original
    2.66 +author's reputation will not be affected by problems that might be
    2.67 +introduced by others.
    2.68 +
    2.69 +  Finally, software patents pose a constant threat to the existence of
    2.70 +any free program.  We wish to make sure that a company cannot
    2.71 +effectively restrict the users of a free program by obtaining a
    2.72 +restrictive license from a patent holder.  Therefore, we insist that
    2.73 +any patent license obtained for a version of the library must be
    2.74 +consistent with the full freedom of use specified in this license.
    2.75 +
    2.76 +  Most GNU software, including some libraries, is covered by the
    2.77 +ordinary GNU General Public License.  This license, the GNU Lesser
    2.78 +General Public License, applies to certain designated libraries, and
    2.79 +is quite different from the ordinary General Public License.  We use
    2.80 +this license for certain libraries in order to permit linking those
    2.81 +libraries into non-free programs.
    2.82 +
    2.83 +  When a program is linked with a library, whether statically or using
    2.84 +a shared library, the combination of the two is legally speaking a
    2.85 +combined work, a derivative of the original library.  The ordinary
    2.86 +General Public License therefore permits such linking only if the
    2.87 +entire combination fits its criteria of freedom.  The Lesser General
    2.88 +Public License permits more lax criteria for linking other code with
    2.89 +the library.
    2.90 +
    2.91 +  We call this license the "Lesser" General Public License because it
    2.92 +does Less to protect the user's freedom than the ordinary General
    2.93 +Public License.  It also provides other free software developers Less
    2.94 +of an advantage over competing non-free programs.  These disadvantages
    2.95 +are the reason we use the ordinary General Public License for many
    2.96 +libraries.  However, the Lesser license provides advantages in certain
    2.97 +special circumstances.
    2.98 +
    2.99 +  For example, on rare occasions, there may be a special need to
   2.100 +encourage the widest possible use of a certain library, so that it becomes
   2.101 +a de-facto standard.  To achieve this, non-free programs must be
   2.102 +allowed to use the library.  A more frequent case is that a free
   2.103 +library does the same job as widely used non-free libraries.  In this
   2.104 +case, there is little to gain by limiting the free library to free
   2.105 +software only, so we use the Lesser General Public License.
   2.106 +
   2.107 +  In other cases, permission to use a particular library in non-free
   2.108 +programs enables a greater number of people to use a large body of
   2.109 +free software.  For example, permission to use the GNU C Library in
   2.110 +non-free programs enables many more people to use the whole GNU
   2.111 +operating system, as well as its variant, the GNU/Linux operating
   2.112 +system.
   2.113 +
   2.114 +  Although the Lesser General Public License is Less protective of the
   2.115 +users' freedom, it does ensure that the user of a program that is
   2.116 +linked with the Library has the freedom and the wherewithal to run
   2.117 +that program using a modified version of the Library.
   2.118 +
   2.119 +  The precise terms and conditions for copying, distribution and
   2.120 +modification follow.  Pay close attention to the difference between a
   2.121 +"work based on the library" and a "work that uses the library".  The
   2.122 +former contains code derived from the library, whereas the latter must
   2.123 +be combined with the library in order to run.
   2.124 +
   2.125 +          GNU LESSER GENERAL PUBLIC LICENSE
   2.126 +   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
   2.127 +
   2.128 +  0. This License Agreement applies to any software library or other
   2.129 +program which contains a notice placed by the copyright holder or
   2.130 +other authorized party saying it may be distributed under the terms of
   2.131 +this Lesser General Public License (also called "this License").
   2.132 +Each licensee is addressed as "you".
   2.133 +
   2.134 +  A "library" means a collection of software functions and/or data
   2.135 +prepared so as to be conveniently linked with application programs
   2.136 +(which use some of those functions and data) to form executables.
   2.137 +
   2.138 +  The "Library", below, refers to any such software library or work
   2.139 +which has been distributed under these terms.  A "work based on the
   2.140 +Library" means either the Library or any derivative work under
   2.141 +copyright law: that is to say, a work containing the Library or a
   2.142 +portion of it, either verbatim or with modifications and/or translated
   2.143 +straightforwardly into another language.  (Hereinafter, translation is
   2.144 +included without limitation in the term "modification".)
   2.145 +
   2.146 +  "Source code" for a work means the preferred form of the work for
   2.147 +making modifications to it.  For a library, complete source code means
   2.148 +all the source code for all modules it contains, plus any associated
   2.149 +interface definition files, plus the scripts used to control compilation
   2.150 +and installation of the library.
   2.151 +
   2.152 +  Activities other than copying, distribution and modification are not
   2.153 +covered by this License; they are outside its scope.  The act of
   2.154 +running a program using the Library is not restricted, and output from
   2.155 +such a program is covered only if its contents constitute a work based
   2.156 +on the Library (independent of the use of the Library in a tool for
   2.157 +writing it).  Whether that is true depends on what the Library does
   2.158 +and what the program that uses the Library does.
   2.159 +
   2.160 +  1. You may copy and distribute verbatim copies of the Library's
   2.161 +complete source code as you receive it, in any medium, provided that
   2.162 +you conspicuously and appropriately publish on each copy an
   2.163 +appropriate copyright notice and disclaimer of warranty; keep intact
   2.164 +all the notices that refer to this License and to the absence of any
   2.165 +warranty; and distribute a copy of this License along with the
   2.166 +Library.
   2.167 +
   2.168 +  You may charge a fee for the physical act of transferring a copy,
   2.169 +and you may at your option offer warranty protection in exchange for a
   2.170 +fee.
   2.171 +
   2.172 +  2. You may modify your copy or copies of the Library or any portion
   2.173 +of it, thus forming a work based on the Library, and copy and
   2.174 +distribute such modifications or work under the terms of Section 1
   2.175 +above, provided that you also meet all of these conditions:
   2.176 +
   2.177 +    a) The modified work must itself be a software library.
   2.178 +
   2.179 +    b) You must cause the files modified to carry prominent notices
   2.180 +    stating that you changed the files and the date of any change.
   2.181 +
   2.182 +    c) You must cause the whole of the work to be licensed at no
   2.183 +    charge to all third parties under the terms of this License.
   2.184 +
   2.185 +    d) If a facility in the modified Library refers to a function or a
   2.186 +    table of data to be supplied by an application program that uses
   2.187 +    the facility, other than as an argument passed when the facility
   2.188 +    is invoked, then you must make a good faith effort to ensure that,
   2.189 +    in the event an application does not supply such function or
   2.190 +    table, the facility still operates, and performs whatever part of
   2.191 +    its purpose remains meaningful.
   2.192 +
   2.193 +    (For example, a function in a library to compute square roots has
   2.194 +    a purpose that is entirely well-defined independent of the
   2.195 +    application.  Therefore, Subsection 2d requires that any
   2.196 +    application-supplied function or table used by this function must
   2.197 +    be optional: if the application does not supply it, the square
   2.198 +    root function must still compute square roots.)
   2.199 +
   2.200 +These requirements apply to the modified work as a whole.  If
   2.201 +identifiable sections of that work are not derived from the Library,
   2.202 +and can be reasonably considered independent and separate works in
   2.203 +themselves, then this License, and its terms, do not apply to those
   2.204 +sections when you distribute them as separate works.  But when you
   2.205 +distribute the same sections as part of a whole which is a work based
   2.206 +on the Library, the distribution of the whole must be on the terms of
   2.207 +this License, whose permissions for other licensees extend to the
   2.208 +entire whole, and thus to each and every part regardless of who wrote
   2.209 +it.
   2.210 +
   2.211 +Thus, it is not the intent of this section to claim rights or contest
   2.212 +your rights to work written entirely by you; rather, the intent is to
   2.213 +exercise the right to control the distribution of derivative or
   2.214 +collective works based on the Library.
   2.215 +
   2.216 +In addition, mere aggregation of another work not based on the Library
   2.217 +with the Library (or with a work based on the Library) on a volume of
   2.218 +a storage or distribution medium does not bring the other work under
   2.219 +the scope of this License.
   2.220 +
   2.221 +  3. You may opt to apply the terms of the ordinary GNU General Public
   2.222 +License instead of this License to a given copy of the Library.  To do
   2.223 +this, you must alter all the notices that refer to this License, so
   2.224 +that they refer to the ordinary GNU General Public License, version 2,
   2.225 +instead of to this License.  (If a newer version than version 2 of the
   2.226 +ordinary GNU General Public License has appeared, then you can specify
   2.227 +that version instead if you wish.)  Do not make any other change in
   2.228 +these notices.
   2.229 +
   2.230 +  Once this change is made in a given copy, it is irreversible for
   2.231 +that copy, so the ordinary GNU General Public License applies to all
   2.232 +subsequent copies and derivative works made from that copy.
   2.233 +
   2.234 +  This option is useful when you wish to copy part of the code of
   2.235 +the Library into a program that is not a library.
   2.236 +
   2.237 +  4. You may copy and distribute the Library (or a portion or
   2.238 +derivative of it, under Section 2) in object code or executable form
   2.239 +under the terms of Sections 1 and 2 above provided that you accompany
   2.240 +it with the complete corresponding machine-readable source code, which
   2.241 +must be distributed under the terms of Sections 1 and 2 above on a
   2.242 +medium customarily used for software interchange.
   2.243 +
   2.244 +  If distribution of object code is made by offering access to copy
   2.245 +from a designated place, then offering equivalent access to copy the
   2.246 +source code from the same place satisfies the requirement to
   2.247 +distribute the source code, even though third parties are not
   2.248 +compelled to copy the source along with the object code.
   2.249 +
   2.250 +  5. A program that contains no derivative of any portion of the
   2.251 +Library, but is designed to work with the Library by being compiled or
   2.252 +linked with it, is called a "work that uses the Library".  Such a
   2.253 +work, in isolation, is not a derivative work of the Library, and
   2.254 +therefore falls outside the scope of this License.
   2.255 +
   2.256 +  However, linking a "work that uses the Library" with the Library
   2.257 +creates an executable that is a derivative of the Library (because it
   2.258 +contains portions of the Library), rather than a "work that uses the
   2.259 +library".  The executable is therefore covered by this License.
   2.260 +Section 6 states terms for distribution of such executables.
   2.261 +
   2.262 +  When a "work that uses the Library" uses material from a header file
   2.263 +that is part of the Library, the object code for the work may be a
   2.264 +derivative work of the Library even though the source code is not.
   2.265 +Whether this is true is especially significant if the work can be
   2.266 +linked without the Library, or if the work is itself a library.  The
   2.267 +threshold for this to be true is not precisely defined by law.
   2.268 +
   2.269 +  If such an object file uses only numerical parameters, data
   2.270 +structure layouts and accessors, and small macros and small inline
   2.271 +functions (ten lines or less in length), then the use of the object
   2.272 +file is unrestricted, regardless of whether it is legally a derivative
   2.273 +work.  (Executables containing this object code plus portions of the
   2.274 +Library will still fall under Section 6.)
   2.275 +
   2.276 +  Otherwise, if the work is a derivative of the Library, you may
   2.277 +distribute the object code for the work under the terms of Section 6.
   2.278 +Any executables containing that work also fall under Section 6,
   2.279 +whether or not they are linked directly with the Library itself.
   2.280 +
   2.281 +  6. As an exception to the Sections above, you may also combine or
   2.282 +link a "work that uses the Library" with the Library to produce a
   2.283 +work containing portions of the Library, and distribute that work
   2.284 +under terms of your choice, provided that the terms permit
   2.285 +modification of the work for the customer's own use and reverse
   2.286 +engineering for debugging such modifications.
   2.287 +
   2.288 +  You must give prominent notice with each copy of the work that the
   2.289 +Library is used in it and that the Library and its use are covered by
   2.290 +this License.  You must supply a copy of this License.  If the work
   2.291 +during execution displays copyright notices, you must include the
   2.292 +copyright notice for the Library among them, as well as a reference
   2.293 +directing the user to the copy of this License.  Also, you must do one
   2.294 +of these things:
   2.295 +
   2.296 +    a) Accompany the work with the complete corresponding
   2.297 +    machine-readable source code for the Library including whatever
   2.298 +    changes were used in the work (which must be distributed under
   2.299 +    Sections 1 and 2 above); and, if the work is an executable linked
   2.300 +    with the Library, with the complete machine-readable "work that
   2.301 +    uses the Library", as object code and/or source code, so that the
   2.302 +    user can modify the Library and then relink to produce a modified
   2.303 +    executable containing the modified Library.  (It is understood
   2.304 +    that the user who changes the contents of definitions files in the
   2.305 +    Library will not necessarily be able to recompile the application
   2.306 +    to use the modified definitions.)
   2.307 +
   2.308 +    b) Use a suitable shared library mechanism for linking with the
   2.309 +    Library.  A suitable mechanism is one that (1) uses at run time a
   2.310 +    copy of the library already present on the user's computer system,
   2.311 +    rather than copying library functions into the executable, and (2)
   2.312 +    will operate properly with a modified version of the library, if
   2.313 +    the user installs one, as long as the modified version is
   2.314 +    interface-compatible with the version that the work was made with.
   2.315 +
   2.316 +    c) Accompany the work with a written offer, valid for at
   2.317 +    least three years, to give the same user the materials
   2.318 +    specified in Subsection 6a, above, for a charge no more
   2.319 +    than the cost of performing this distribution.
   2.320 +
   2.321 +    d) If distribution of the work is made by offering access to copy
   2.322 +    from a designated place, offer equivalent access to copy the above
   2.323 +    specified materials from the same place.
   2.324 +
   2.325 +    e) Verify that the user has already received a copy of these
   2.326 +    materials or that you have already sent this user a copy.
   2.327 +
   2.328 +  For an executable, the required form of the "work that uses the
   2.329 +Library" must include any data and utility programs needed for
   2.330 +reproducing the executable from it.  However, as a special exception,
   2.331 +the materials to be distributed need not include anything that is
   2.332 +normally distributed (in either source or binary form) with the major
   2.333 +components (compiler, kernel, and so on) of the operating system on
   2.334 +which the executable runs, unless that component itself accompanies
   2.335 +the executable.
   2.336 +
   2.337 +  It may happen that this requirement contradicts the license
   2.338 +restrictions of other proprietary libraries that do not normally
   2.339 +accompany the operating system.  Such a contradiction means you cannot
   2.340 +use both them and the Library together in an executable that you
   2.341 +distribute.
   2.342 +
   2.343 +  7. You may place library facilities that are a work based on the
   2.344 +Library side-by-side in a single library together with other library
   2.345 +facilities not covered by this License, and distribute such a combined
   2.346 +library, provided that the separate distribution of the work based on
   2.347 +the Library and of the other library facilities is otherwise
   2.348 +permitted, and provided that you do these two things:
   2.349 +
   2.350 +    a) Accompany the combined library with a copy of the same work
   2.351 +    based on the Library, uncombined with any other library
   2.352 +    facilities.  This must be distributed under the terms of the
   2.353 +    Sections above.
   2.354 +
   2.355 +    b) Give prominent notice with the combined library of the fact
   2.356 +    that part of it is a work based on the Library, and explaining
   2.357 +    where to find the accompanying uncombined form of the same work.
   2.358 +
   2.359 +  8. You may not copy, modify, sublicense, link with, or distribute
   2.360 +the Library except as expressly provided under this License.  Any
   2.361 +attempt otherwise to copy, modify, sublicense, link with, or
   2.362 +distribute the Library is void, and will automatically terminate your
   2.363 +rights under this License.  However, parties who have received copies,
   2.364 +or rights, from you under this License will not have their licenses
   2.365 +terminated so long as such parties remain in full compliance.
   2.366 +
   2.367 +  9. You are not required to accept this License, since you have not
   2.368 +signed it.  However, nothing else grants you permission to modify or
   2.369 +distribute the Library or its derivative works.  These actions are
   2.370 +prohibited by law if you do not accept this License.  Therefore, by
   2.371 +modifying or distributing the Library (or any work based on the
   2.372 +Library), you indicate your acceptance of this License to do so, and
   2.373 +all its terms and conditions for copying, distributing or modifying
   2.374 +the Library or works based on it.
   2.375 +
   2.376 +  10. Each time you redistribute the Library (or any work based on the
   2.377 +Library), the recipient automatically receives a license from the
   2.378 +original licensor to copy, distribute, link with or modify the Library
   2.379 +subject to these terms and conditions.  You may not impose any further
   2.380 +restrictions on the recipients' exercise of the rights granted herein.
   2.381 +You are not responsible for enforcing compliance by third parties with
   2.382 +this License.
   2.383 +
   2.384 +  11. If, as a consequence of a court judgment or allegation of patent
   2.385 +infringement or for any other reason (not limited to patent issues),
   2.386 +conditions are imposed on you (whether by court order, agreement or
   2.387 +otherwise) that contradict the conditions of this License, they do not
   2.388 +excuse you from the conditions of this License.  If you cannot
   2.389 +distribute so as to satisfy simultaneously your obligations under this
   2.390 +License and any other pertinent obligations, then as a consequence you
   2.391 +may not distribute the Library at all.  For example, if a patent
   2.392 +license would not permit royalty-free redistribution of the Library by
   2.393 +all those who receive copies directly or indirectly through you, then
   2.394 +the only way you could satisfy both it and this License would be to
   2.395 +refrain entirely from distribution of the Library.
   2.396 +
   2.397 +If any portion of this section is held invalid or unenforceable under any
   2.398 +particular circumstance, the balance of the section is intended to apply,
   2.399 +and the section as a whole is intended to apply in other circumstances.
   2.400 +
   2.401 +It is not the purpose of this section to induce you to infringe any
   2.402 +patents or other property right claims or to contest validity of any
   2.403 +such claims; this section has the sole purpose of protecting the
   2.404 +integrity of the free software distribution system which is
   2.405 +implemented by public license practices.  Many people have made
   2.406 +generous contributions to the wide range of software distributed
   2.407 +through that system in reliance on consistent application of that
   2.408 +system; it is up to the author/donor to decide if he or she is willing
   2.409 +to distribute software through any other system and a licensee cannot
   2.410 +impose that choice.
   2.411 +
   2.412 +This section is intended to make thoroughly clear what is believed to
   2.413 +be a consequence of the rest of this License.
   2.414 +
   2.415 +  12. If the distribution and/or use of the Library is restricted in
   2.416 +certain countries either by patents or by copyrighted interfaces, the
   2.417 +original copyright holder who places the Library under this License may add
   2.418 +an explicit geographical distribution limitation excluding those countries,
   2.419 +so that distribution is permitted only in or among countries not thus
   2.420 +excluded.  In such case, this License incorporates the limitation as if
   2.421 +written in the body of this License.
   2.422 +
   2.423 +  13. The Free Software Foundation may publish revised and/or new
   2.424 +versions of the Lesser General Public License from time to time.
   2.425 +Such new versions will be similar in spirit to the present version,
   2.426 +but may differ in detail to address new problems or concerns.
   2.427 +
   2.428 +Each version is given a distinguishing version number.  If the Library
   2.429 +specifies a version number of this License which applies to it and
   2.430 +"any later version", you have the option of following the terms and
   2.431 +conditions either of that version or of any later version published by
   2.432 +the Free Software Foundation.  If the Library does not specify a
   2.433 +license version number, you may choose any version ever published by
   2.434 +the Free Software Foundation.
   2.435 +
   2.436 +  14. If you wish to incorporate parts of the Library into other free
   2.437 +programs whose distribution conditions are incompatible with these,
   2.438 +write to the author to ask for permission.  For software which is
   2.439 +copyrighted by the Free Software Foundation, write to the Free
   2.440 +Software Foundation; we sometimes make exceptions for this.  Our
   2.441 +decision will be guided by the two goals of preserving the free status
   2.442 +of all derivatives of our free software and of promoting the sharing
   2.443 +and reuse of software generally.
   2.444 +
   2.445 +                NO WARRANTY
   2.446 +
   2.447 +  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
   2.448 +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
   2.449 +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
   2.450 +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
   2.451 +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
   2.452 +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   2.453 +PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
   2.454 +LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
   2.455 +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
   2.456 +
   2.457 +  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
   2.458 +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
   2.459 +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
   2.460 +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
   2.461 +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
   2.462 +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
   2.463 +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
   2.464 +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
   2.465 +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
   2.466 +DAMAGES.
   2.467 +
   2.468 +             END OF TERMS AND CONDITIONS
   2.469 +
   2.470 +           How to Apply These Terms to Your New Libraries
   2.471 +
   2.472 +  If you develop a new library, and you want it to be of the greatest
   2.473 +possible use to the public, we recommend making it free software that
   2.474 +everyone can redistribute and change.  You can do so by permitting
   2.475 +redistribution under these terms (or, alternatively, under the terms of the
   2.476 +ordinary General Public License).
   2.477 +
   2.478 +  To apply these terms, attach the following notices to the library.  It is
   2.479 +safest to attach them to the start of each source file to most effectively
   2.480 +convey the exclusion of warranty; and each file should have at least the
   2.481 +"copyright" line and a pointer to where the full notice is found.
   2.482 +
   2.483 +    <one line to give the library's name and a brief idea of what it does.>
   2.484 +    Copyright (C) <year>  <name of author>
   2.485 +
   2.486 +    This library is free software; you can redistribute it and/or
   2.487 +    modify it under the terms of the GNU Lesser General Public
   2.488 +    License as published by the Free Software Foundation; either
   2.489 +    version 2.1 of the License, or (at your option) any later version.
   2.490 +
   2.491 +    This library is distributed in the hope that it will be useful,
   2.492 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
   2.493 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   2.494 +    Lesser General Public License for more details.
   2.495 +
   2.496 +    You should have received a copy of the GNU Lesser General Public
   2.497 +    License along with this library; if not, write to the Free Software
   2.498 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
   2.499 +
   2.500 +Also add information on how to contact you by electronic and paper mail.
   2.501 +
   2.502 +You should also get your employer (if you work as a programmer) or your
   2.503 +school, if any, to sign a "copyright disclaimer" for the library, if
   2.504 +necessary.  Here is a sample; alter the names:
   2.505 +
   2.506 +  Yoyodyne, Inc., hereby disclaims all copyright interest in the
   2.507 +  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
   2.508 +
   2.509 +  <signature of Ty Coon>, 1 April 1990
   2.510 +  Ty Coon, President of Vice
     3.1 --- a/docker.api/manifest.mf	Wed Mar 23 21:02:01 2016 +0000
     3.2 +++ b/docker.api/manifest.mf	Thu Mar 24 15:34:03 2016 +0100
     3.3 @@ -1,6 +1,6 @@
     3.4  Manifest-Version: 1.0
     3.5  OpenIDE-Module: org.netbeans.modules.docker.api/0
     3.6  OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/docker/resources/Bundle.properties
     3.7 -OpenIDE-Module-Specification-Version: 1.16
     3.8 +OpenIDE-Module-Specification-Version: 1.19
     3.9  AutoUpdate-Show-In-Client: false
    3.10  
     4.1 --- a/docker.api/nbproject/project.properties	Wed Mar 23 21:02:01 2016 +0000
     4.2 +++ b/docker.api/nbproject/project.properties	Thu Mar 24 15:34:03 2016 +0100
     4.3 @@ -44,3 +44,5 @@
     4.4  is.autoload=true
     4.5  javac.compilerargs=-Xlint -Xlint:-serial
     4.6  javac.source=1.8
     4.7 +
     4.8 +release.external/juds-0.95.jar=modules/ext/juds-0.95.jar
     5.1 --- a/docker.api/nbproject/project.xml	Wed Mar 23 21:02:01 2016 +0000
     5.2 +++ b/docker.api/nbproject/project.xml	Thu Mar 24 15:34:03 2016 +0100
     5.3 @@ -94,6 +94,14 @@
     5.4                      </run-dependency>
     5.5                  </dependency>
     5.6                  <dependency>
     5.7 +                    <code-name-base>org.openide.modules</code-name-base>
     5.8 +                    <build-prerequisite/>
     5.9 +                    <compile-dependency/>
    5.10 +                    <run-dependency>
    5.11 +                        <specification-version>7.48</specification-version>
    5.12 +                    </run-dependency>
    5.13 +                </dependency>
    5.14 +                <dependency>
    5.15                      <code-name-base>org.openide.util</code-name-base>
    5.16                      <build-prerequisite/>
    5.17                      <compile-dependency/>
    5.18 @@ -138,6 +146,10 @@
    5.19                  <friend>org.netbeans.modules.docker.ui</friend>
    5.20                  <package>org.netbeans.modules.docker.api</package>
    5.21              </friend-packages>
    5.22 +            <class-path-extension>
    5.23 +                <runtime-relative-path>ext/juds-0.95.jar</runtime-relative-path>
    5.24 +                <binary-origin>external/juds-0.95.jar</binary-origin>
    5.25 +            </class-path-extension>
    5.26          </data>
    5.27      </configuration>
    5.28  </project>
     6.1 --- a/docker.api/src/org/netbeans/modules/docker/ConnectionListener.java	Wed Mar 23 21:02:01 2016 +0000
     6.2 +++ b/docker.api/src/org/netbeans/modules/docker/ConnectionListener.java	Thu Mar 24 15:34:03 2016 +0100
     6.3 @@ -41,15 +41,13 @@
     6.4   */
     6.5  package org.netbeans.modules.docker;
     6.6  
     6.7 -import java.net.Socket;
     6.8 -
     6.9  /**
    6.10   *
    6.11   * @author Petr Hejl
    6.12   */
    6.13  public interface ConnectionListener {
    6.14  
    6.15 -    void onConnect(Socket s);
    6.16 +    void onConnect(Endpoint e);
    6.17  
    6.18      void onDisconnect();
    6.19      
     7.1 --- a/docker.api/src/org/netbeans/modules/docker/DirectStreamResult.java	Wed Mar 23 21:02:01 2016 +0000
     7.2 +++ b/docker.api/src/org/netbeans/modules/docker/DirectStreamResult.java	Thu Mar 24 15:34:03 2016 +0100
     7.3 @@ -53,7 +53,7 @@
     7.4   */
     7.5  public class DirectStreamResult implements StreamResult {
     7.6  
     7.7 -    private final Socket s;
     7.8 +    private final Endpoint s;
     7.9  
    7.10      private final Charset charset;
    7.11  
    7.12 @@ -63,7 +63,7 @@
    7.13  
    7.14      private final InputStream stdErr;
    7.15  
    7.16 -    public DirectStreamResult(Socket s, Charset charset, InputStream is) throws IOException {
    7.17 +    public DirectStreamResult(Endpoint s, Charset charset, InputStream is) throws IOException {
    7.18          this.s = s;
    7.19          this.charset = charset;
    7.20          this.stdIn = s.getOutputStream();
     8.1 --- a/docker.api/src/org/netbeans/modules/docker/DockerEventBus.java	Wed Mar 23 21:02:01 2016 +0000
     8.2 +++ b/docker.api/src/org/netbeans/modules/docker/DockerEventBus.java	Thu Mar 24 15:34:03 2016 +0100
     8.3 @@ -43,7 +43,6 @@
     8.4  
     8.5  import java.io.Closeable;
     8.6  import java.io.IOException;
     8.7 -import java.net.Socket;
     8.8  import java.util.ArrayList;
     8.9  import java.util.List;
    8.10  import java.util.logging.Level;
    8.11 @@ -76,7 +75,7 @@
    8.12  
    8.13      private final List<DockerEvent.Listener> containerListeners = new ArrayList<>();
    8.14  
    8.15 -    private Socket socket;
    8.16 +    private Endpoint endpoint;
    8.17  
    8.18      private DockerEvent lastEvent;
    8.19  
    8.20 @@ -173,10 +172,10 @@
    8.21      }
    8.22  
    8.23      @Override
    8.24 -    public void onConnect(Socket s) {
    8.25 +    public void onConnect(Endpoint e) {
    8.26          List<DockerInstance.ConnectionListener> toFire;
    8.27          synchronized (this) {
    8.28 -            this.socket = s;
    8.29 +            this.endpoint = e;
    8.30              toFire = new ArrayList<>(connectionListeners);
    8.31          }
    8.32          for (DockerInstance.ConnectionListener l : toFire) {
    8.33 @@ -258,11 +257,11 @@
    8.34  
    8.35      private void stop() {
    8.36          // FIXME close the socket to stop waiting for events
    8.37 -        Socket current;
    8.38 +        Endpoint current;
    8.39          synchronized (this) {
    8.40              stop = true;
    8.41 -            current = socket;
    8.42 -            socket = null;
    8.43 +            current = endpoint;
    8.44 +            endpoint = null;
    8.45          }
    8.46          try {
    8.47              if (current != null) {
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/docker.api/src/org/netbeans/modules/docker/Endpoint.java	Thu Mar 24 15:34:03 2016 +0100
     9.3 @@ -0,0 +1,98 @@
     9.4 +/*
     9.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     9.6 + *
     9.7 + * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
     9.8 + *
     9.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    9.10 + * Other names may be trademarks of their respective owners.
    9.11 + *
    9.12 + * The contents of this file are subject to the terms of either the GNU
    9.13 + * General Public License Version 2 only ("GPL") or the Common
    9.14 + * Development and Distribution License("CDDL") (collectively, the
    9.15 + * "License"). You may not use this file except in compliance with the
    9.16 + * License. You can obtain a copy of the License at
    9.17 + * http://www.netbeans.org/cddl-gplv2.html
    9.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    9.19 + * specific language governing permissions and limitations under the
    9.20 + * License.  When distributing the software, include this License Header
    9.21 + * Notice in each file and include the License file at
    9.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    9.23 + * particular file as subject to the "Classpath" exception as provided
    9.24 + * by Oracle in the GPL Version 2 section of the License file that
    9.25 + * accompanied this code. If applicable, add the following below the
    9.26 + * License Header, with the fields enclosed by brackets [] replaced by
    9.27 + * your own identifying information:
    9.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    9.29 + *
    9.30 + * If you wish your version of this file to be governed by only the CDDL
    9.31 + * or only the GPL Version 2, indicate your decision by adding
    9.32 + * "[Contributor] elects to include this software in this distribution
    9.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    9.34 + * single choice of license, a recipient has the option to distribute
    9.35 + * your version of this file under either the CDDL, the GPL Version 2 or
    9.36 + * to extend the choice of license to its licensees as provided above.
    9.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    9.38 + * Version 2 license, then the option applies only if the new code is
    9.39 + * made subject to such option by the copyright holder.
    9.40 + *
    9.41 + * Contributor(s):
    9.42 + *
    9.43 + * Portions Copyrighted 2016 Sun Microsystems, Inc.
    9.44 + */
    9.45 +package org.netbeans.modules.docker;
    9.46 +
    9.47 +import com.etsy.net.UnixDomainSocketClient;
    9.48 +import java.io.Closeable;
    9.49 +import java.io.IOException;
    9.50 +import java.io.InputStream;
    9.51 +import java.io.OutputStream;
    9.52 +import java.net.Socket;
    9.53 +
    9.54 +/**
    9.55 + *
    9.56 + * @author Petr Hejl
    9.57 + */
    9.58 +public interface Endpoint extends Closeable {
    9.59 +
    9.60 +    InputStream getInputStream() throws IOException;
    9.61 +
    9.62 +    OutputStream getOutputStream() throws IOException;
    9.63 +
    9.64 +    public static Endpoint forSocket(final Socket s) {
    9.65 +        return new Endpoint() {
    9.66 +            @Override
    9.67 +            public InputStream getInputStream() throws IOException {
    9.68 +                return s.getInputStream();
    9.69 +            }
    9.70 +
    9.71 +            @Override
    9.72 +            public OutputStream getOutputStream() throws IOException {
    9.73 +                return s.getOutputStream();
    9.74 +            }
    9.75 +
    9.76 +            @Override
    9.77 +            public void close() throws IOException {
    9.78 +                s.close();
    9.79 +            }
    9.80 +        };
    9.81 +    }
    9.82 +
    9.83 +    public static Endpoint forDomainSocket(final UnixDomainSocketClient s) {
    9.84 +        return new Endpoint() {
    9.85 +            @Override
    9.86 +            public InputStream getInputStream() throws IOException {
    9.87 +                return s.getInputStream();
    9.88 +            }
    9.89 +
    9.90 +            @Override
    9.91 +            public OutputStream getOutputStream() throws IOException {
    9.92 +                return s.getOutputStream();
    9.93 +            }
    9.94 +
    9.95 +            @Override
    9.96 +            public void close() throws IOException {
    9.97 +                s.close();
    9.98 +            }
    9.99 +        };
   9.100 +    }
   9.101 +}
    10.1 --- a/docker.api/src/org/netbeans/modules/docker/HttpUtils.java	Wed Mar 23 21:02:01 2016 +0000
    10.2 +++ b/docker.api/src/org/netbeans/modules/docker/HttpUtils.java	Thu Mar 24 15:34:03 2016 +0100
    10.3 @@ -43,11 +43,11 @@
    10.4  
    10.5  import java.io.BufferedInputStream;
    10.6  import java.io.ByteArrayOutputStream;
    10.7 +import java.io.FilterInputStream;
    10.8  import java.io.IOException;
    10.9  import java.io.InputStream;
   10.10  import java.io.OutputStream;
   10.11  import java.io.UnsupportedEncodingException;
   10.12 -import java.net.HttpURLConnection;
   10.13  import java.net.URLEncoder;
   10.14  import java.nio.charset.Charset;
   10.15  import java.nio.charset.UnsupportedCharsetException;
   10.16 @@ -58,6 +58,7 @@
   10.17  import java.util.regex.Matcher;
   10.18  import java.util.regex.Pattern;
   10.19  import org.netbeans.api.annotations.common.CheckForNull;
   10.20 +import org.netbeans.api.annotations.common.NonNull;
   10.21  import org.openide.util.Pair;
   10.22  
   10.23  /**
   10.24 @@ -107,26 +108,45 @@
   10.25          return new String(content, encoding);
   10.26      }
   10.27  
   10.28 -    public static String readError(HttpURLConnection conn) throws IOException {
   10.29 -        InputStream err = conn.getErrorStream();
   10.30 -        if (err == null || err.available() <= 0) {
   10.31 -            return null;
   10.32 -        }
   10.33 -        Charset encoding = getCharset(conn.getContentType());
   10.34 -        ByteArrayOutputStream bos = new ByteArrayOutputStream(200);
   10.35 -        byte[] content = new byte[200];
   10.36 -        int length;
   10.37 -        while((length = err.read(content)) != -1) {
   10.38 -            bos.write(content, 0, length);
   10.39 -        }
   10.40 -        return bos.toString(encoding.name());
   10.41 -    }
   10.42 -
   10.43 -    public static InputStream getResponseStream(InputStream is, Response response) {
   10.44 +    public static InputStream getResponseStream(InputStream is, Response response, boolean allowSocketStream) throws IOException {
   10.45          if (isChunked(response.getHeaders())) {
   10.46              return new ChunkedInputStream(new BufferedInputStream(is));
   10.47          } else {
   10.48 -            return new BufferedInputStream(is);
   10.49 +            Integer l = getLength(response.getHeaders());
   10.50 +            if (l != null) {
   10.51 +                return new BufferedInputStream(new FilterInputStream(is) {
   10.52 +                    private int available = l;
   10.53 +
   10.54 +                    @Override
   10.55 +                    public int available() throws IOException {
   10.56 +                        return Math.min(super.available(), available);
   10.57 +                    }
   10.58 +
   10.59 +                    @Override
   10.60 +                    public int read(byte[] b, int off, int len) throws IOException {
   10.61 +                        if (available <= 0) {
   10.62 +                            return -1;
   10.63 +                        }
   10.64 +                        int real = Math.min(available, len);
   10.65 +                        int count = super.read(b, off, real);
   10.66 +                        available -= count;
   10.67 +                        return count;
   10.68 +                    }
   10.69 +
   10.70 +                    @Override
   10.71 +                    public int read() throws IOException {
   10.72 +                        if (available <= 0) {
   10.73 +                            return -1;
   10.74 +                        }
   10.75 +                        available--;
   10.76 +                        return super.read();
   10.77 +                    }
   10.78 +                });
   10.79 +            } else if (allowSocketStream) {
   10.80 +                return new BufferedInputStream(is);
   10.81 +            } else {
   10.82 +                throw new IOException("Undefined content length");
   10.83 +            }
   10.84          }
   10.85      }
   10.86  
   10.87 @@ -142,14 +162,11 @@
   10.88          }
   10.89      }
   10.90  
   10.91 +    @NonNull
   10.92      public static Charset getCharset(Response response) {
   10.93          return getCharset(response.getHeaders().get("CONTENT-TYPE")); // NOI18N
   10.94      }
   10.95  
   10.96 -    public static Charset getCharset(HttpURLConnection connection) {
   10.97 -        return getCharset(connection.getContentType());
   10.98 -    }
   10.99 -
  10.100      public static String encodeParameter(String value) throws UnsupportedEncodingException {
  10.101          return URLEncoder.encode(value, "UTF-8");
  10.102      }
  10.103 @@ -181,20 +198,6 @@
  10.104          }
  10.105      }
  10.106  
  10.107 -    public static void configureHeaders(HttpURLConnection conn, Map<String, String> defaultHeaders,
  10.108 -            Pair<String, String>... headers) {
  10.109 -
  10.110 -        for (Map.Entry<String, String> e : defaultHeaders.entrySet()) {
  10.111 -            conn.setRequestProperty(e.getKey(), e.getValue());
  10.112 -        }
  10.113 -        for (Pair<String, String> h : headers) {
  10.114 -            if (h == null) {
  10.115 -                continue;
  10.116 -            }
  10.117 -            conn.setRequestProperty(h.first(), h.second());
  10.118 -        }
  10.119 -    }
  10.120 -
  10.121      static String readResponseLine(InputStream is) throws IOException {
  10.122          ByteArrayOutputStream bos = new ByteArrayOutputStream();
  10.123          int b;
    11.1 --- a/docker.api/src/org/netbeans/modules/docker/MuxedStreamResult.java	Wed Mar 23 21:02:01 2016 +0000
    11.2 +++ b/docker.api/src/org/netbeans/modules/docker/MuxedStreamResult.java	Thu Mar 24 15:34:03 2016 +0100
    11.3 @@ -57,7 +57,7 @@
    11.4  
    11.5      private static final Logger LOGGER = Logger.getLogger(MuxedStreamResult.class.getName());
    11.6  
    11.7 -    private final Socket s;
    11.8 +    private final Endpoint s;
    11.9  
   11.10      private final Charset charset;
   11.11  
   11.12 @@ -71,7 +71,7 @@
   11.13  
   11.14      private StreamItem last = StreamItem.EMPTY;
   11.15  
   11.16 -    public MuxedStreamResult(Socket s, Charset charset, InputStream is) throws IOException {
   11.17 +    public MuxedStreamResult(Endpoint s, Charset charset, InputStream is) throws IOException {
   11.18          this.s = s;
   11.19          this.charset = charset;
   11.20          this.outputStream = s.getOutputStream();
    12.1 --- a/docker.api/src/org/netbeans/modules/docker/api/ActionChunkedResult.java	Wed Mar 23 21:02:01 2016 +0000
    12.2 +++ b/docker.api/src/org/netbeans/modules/docker/api/ActionChunkedResult.java	Thu Mar 24 15:34:03 2016 +0100
    12.3 @@ -43,10 +43,10 @@
    12.4  
    12.5  import java.io.Closeable;
    12.6  import java.io.IOException;
    12.7 -import java.net.Socket;
    12.8  import java.nio.ByteBuffer;
    12.9  import java.nio.charset.Charset;
   12.10  import org.netbeans.api.annotations.common.CheckForNull;
   12.11 +import org.netbeans.modules.docker.Endpoint;
   12.12  import org.netbeans.modules.docker.StreamItem;
   12.13  
   12.14  /**
   12.15 @@ -55,13 +55,13 @@
   12.16   */
   12.17  public final class ActionChunkedResult implements Closeable {
   12.18  
   12.19 -    private final Socket s;
   12.20 +    private final Endpoint s;
   12.21  
   12.22      private final StreamItem.Fetcher fetcher;
   12.23  
   12.24      private final Charset charset;
   12.25  
   12.26 -    ActionChunkedResult(Socket s, StreamItem.Fetcher fetcher, Charset charset) {
   12.27 +    ActionChunkedResult(Endpoint s, StreamItem.Fetcher fetcher, Charset charset) {
   12.28          this.s = s;
   12.29          this.fetcher = fetcher;
   12.30          this.charset = charset;
    13.1 --- a/docker.api/src/org/netbeans/modules/docker/api/DockerAction.java	Wed Mar 23 21:02:01 2016 +0000
    13.2 +++ b/docker.api/src/org/netbeans/modules/docker/api/DockerAction.java	Thu Mar 24 15:34:03 2016 +0100
    13.3 @@ -41,6 +41,8 @@
    13.4   */
    13.5  package org.netbeans.modules.docker.api;
    13.6  
    13.7 +import com.etsy.net.JUDS;
    13.8 +import com.etsy.net.UnixDomainSocketClient;
    13.9  import org.netbeans.modules.docker.StreamItem;
   13.10  import org.netbeans.modules.docker.ConnectionListener;
   13.11  import org.netbeans.modules.docker.DockerRemoteException;
   13.12 @@ -52,7 +54,6 @@
   13.13  import org.netbeans.modules.docker.HttpUtils;
   13.14  import org.netbeans.modules.docker.DirectStreamResult;
   13.15  import org.netbeans.modules.docker.Demuxer;
   13.16 -import java.io.BufferedOutputStream;
   13.17  import java.io.BufferedReader;
   13.18  import java.io.File;
   13.19  import java.io.IOException;
   13.20 @@ -87,7 +88,6 @@
   13.21  import java.util.logging.Logger;
   13.22  import java.util.regex.Matcher;
   13.23  import java.util.regex.Pattern;
   13.24 -import javax.net.ssl.HttpsURLConnection;
   13.25  import javax.net.ssl.SSLContext;
   13.26  import javax.swing.SwingUtilities;
   13.27  import org.json.simple.JSONArray;
   13.28 @@ -99,11 +99,10 @@
   13.29  import org.netbeans.modules.docker.DockerActionAccessor;
   13.30  import org.netbeans.modules.docker.DockerConfig;
   13.31  import org.netbeans.modules.docker.DockerUtils;
   13.32 +import org.netbeans.modules.docker.Endpoint;
   13.33  import org.netbeans.modules.docker.StreamResult;
   13.34 -import org.openide.filesystems.FileUtil;
   13.35  import org.openide.util.Pair;
   13.36  import org.openide.util.Parameters;
   13.37 -import org.openide.util.RequestProcessor;
   13.38  import org.openide.util.io.NullInputStream;
   13.39  import org.openide.util.io.NullOutputStream;
   13.40  
   13.41 @@ -130,6 +129,11 @@
   13.42      private static final Pair<String, String> ACCEPT_JSON_HEADER = Pair.of("Accept", "application/json");
   13.43  
   13.44      static {
   13.45 +        if (System.getProperty("juds.folder.preferred") == null) {
   13.46 +            String userhome = System.getProperty("netbeans.user");
   13.47 +            System.setProperty("juds.folder.preferred", userhome);
   13.48 +        }
   13.49 +
   13.50          DockerActionAccessor.setDefault(new DockerActionAccessor() {
   13.51              @Override
   13.52              public void events(DockerAction action, Long since, DockerEvent.Listener listener, ConnectionListener connectionListener) throws DockerException {
   13.53 @@ -142,8 +146,6 @@
   13.54          Collections.addAll(REMOVE_IMAGE_CODES, HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_NOT_FOUND);
   13.55      }
   13.56  
   13.57 -    private final RequestProcessor requestProcessor = new RequestProcessor(DockerAction.class);
   13.58 -
   13.59      private final DockerInstance instance;
   13.60  
   13.61      private final boolean emitEvents;
   13.62 @@ -160,8 +162,8 @@
   13.63  
   13.64      public List<DockerImage> getImages() {
   13.65          try {
   13.66 -            JSONArray value = (JSONArray) doGetRequest(instance.getUrl(),
   13.67 -                    "/images/json", Collections.singleton(HttpURLConnection.HTTP_OK));
   13.68 +            JSONArray value = (JSONArray) doGetRequest("/images/json",
   13.69 +                    Collections.singleton(HttpURLConnection.HTTP_OK));
   13.70              List<DockerImage> ret = new ArrayList<>(value.size());
   13.71              for (Object o : value) {
   13.72                  JSONObject json  = (JSONObject) o;
   13.73 @@ -181,8 +183,8 @@
   13.74  
   13.75      public List<DockerContainer> getContainers() {
   13.76          try {
   13.77 -            JSONArray value = (JSONArray) doGetRequest(instance.getUrl(),
   13.78 -                    "/containers/json?all=1", Collections.singleton(HttpURLConnection.HTTP_OK));
   13.79 +            JSONArray value = (JSONArray) doGetRequest("/containers/json?all=1",
   13.80 +                    Collections.singleton(HttpURLConnection.HTTP_OK));
   13.81              List<DockerContainer> ret = new ArrayList<>(value.size());
   13.82              for (Object o : value) {
   13.83                  JSONObject json = (JSONObject) o;
   13.84 @@ -210,8 +212,9 @@
   13.85          }
   13.86  
   13.87          try {
   13.88 -            JSONArray value = (JSONArray) doGetRequest(instance.getUrl(),
   13.89 -                    "/images/search?term=" + HttpUtils.encodeParameter(searchTerm), Collections.singleton(HttpURLConnection.HTTP_OK));
   13.90 +            JSONArray value = (JSONArray) doGetRequest(
   13.91 +                    "/images/search?term=" + HttpUtils.encodeParameter(searchTerm),
   13.92 +                    Collections.singleton(HttpURLConnection.HTTP_OK));
   13.93              List<DockerRegistryImage> ret = new ArrayList<>(value.size());
   13.94              for (Object o : value) {
   13.95                  JSONObject json = (JSONObject) o;
   13.96 @@ -256,7 +259,7 @@
   13.97                  action.append("&pause=0");
   13.98              }
   13.99  
  13.100 -            JSONObject value = (JSONObject) doPostRequest(instance.getUrl(), action.toString(), null,
  13.101 +            JSONObject value = (JSONObject) doPostRequest(action.toString(),
  13.102                      true, Collections.singleton(HttpURLConnection.HTTP_CREATED));
  13.103  
  13.104              String id = (String) value.get("Id");
  13.105 @@ -282,8 +285,7 @@
  13.106          Parameters.notNull("name", name);
  13.107  
  13.108          try {
  13.109 -            doPostRequest(instance.getUrl(),
  13.110 -                    "/containers/" + container.getId() + "/rename?name=" + HttpUtils.encodeParameter(name), null,
  13.111 +            doPostRequest("/containers/" + container.getId() + "/rename?name=" + HttpUtils.encodeParameter(name),
  13.112                      false, Collections.singleton(HttpURLConnection.HTTP_NO_CONTENT));
  13.113  
  13.114              long time = System.currentTimeMillis() / 1000;
  13.115 @@ -315,7 +317,7 @@
  13.116              action.append("&tag=").append(tag);
  13.117          }
  13.118  
  13.119 -        doPostRequest(instance.getUrl(), action.toString(), null,
  13.120 +        doPostRequest(action.toString(),
  13.121                  false, Collections.singleton(HttpURLConnection.HTTP_CREATED));
  13.122  
  13.123          String tagResult = DockerUtils.getTag(repository, tag);
  13.124 @@ -331,8 +333,8 @@
  13.125      }
  13.126  
  13.127      public DockerContainerDetail getDetail(DockerContainer container) throws DockerException {
  13.128 -        JSONObject value = (JSONObject) doGetRequest(instance.getUrl(),
  13.129 -                "/containers/" + container.getId() + "/json", Collections.singleton(HttpURLConnection.HTTP_OK));
  13.130 +        JSONObject value = (JSONObject) doGetRequest("/containers/" + container.getId() + "/json",
  13.131 +                Collections.singleton(HttpURLConnection.HTTP_OK));
  13.132          String name = (String) value.get("Name");
  13.133          DockerContainer.Status status = DockerContainer.Status.STOPPED;
  13.134          JSONObject state = (JSONObject) value.get("State");
  13.135 @@ -359,8 +361,8 @@
  13.136      }
  13.137  
  13.138      public DockerImageDetail getDetail(DockerImage image) throws DockerException {
  13.139 -        JSONObject value = (JSONObject) doGetRequest(instance.getUrl(),
  13.140 -                "/images/" + image.getId() + "/json", Collections.singleton(HttpURLConnection.HTTP_OK));
  13.141 +        JSONObject value = (JSONObject) doGetRequest("/images/" + image.getId() + "/json",
  13.142 +                Collections.singleton(HttpURLConnection.HTTP_OK));
  13.143          List<ExposedPort> ports = new LinkedList<>();
  13.144          JSONObject config = (JSONObject) value.get("Config");
  13.145          if (config != null) {
  13.146 @@ -383,7 +385,7 @@
  13.147      }
  13.148  
  13.149      public void start(DockerContainer container) throws DockerException {
  13.150 -        doPostRequest(instance.getUrl(), "/containers/" + container.getId() + "/start", null, false, START_STOP_CONTAINER_CODES);
  13.151 +        doPostRequest("/containers/" + container.getId() + "/start", false, START_STOP_CONTAINER_CODES);
  13.152  
  13.153          if (emitEvents) {
  13.154              instance.getEventBus().sendEvent(
  13.155 @@ -393,7 +395,7 @@
  13.156      }
  13.157  
  13.158      public void stop(DockerContainer container) throws DockerException {
  13.159 -        doPostRequest(instance.getUrl(), "/containers/" + container.getId() + "/stop", null, false, START_STOP_CONTAINER_CODES);
  13.160 +        doPostRequest("/containers/" + container.getId() + "/stop", false, START_STOP_CONTAINER_CODES);
  13.161  
  13.162          if (emitEvents) {
  13.163              instance.getEventBus().sendEvent(
  13.164 @@ -403,7 +405,7 @@
  13.165      }
  13.166  
  13.167      public void pause(DockerContainer container) throws DockerException {
  13.168 -        doPostRequest(instance.getUrl(), "/containers/" + container.getId() + "/pause", null, false,
  13.169 +        doPostRequest("/containers/" + container.getId() + "/pause", false,
  13.170                  Collections.singleton(HttpURLConnection.HTTP_NO_CONTENT));
  13.171  
  13.172          if (emitEvents) {
  13.173 @@ -414,7 +416,7 @@
  13.174      }
  13.175  
  13.176      public void unpause(DockerContainer container) throws DockerException {
  13.177 -        doPostRequest(instance.getUrl(), "/containers/" + container.getId() + "/unpause", null, false,
  13.178 +        doPostRequest("/containers/" + container.getId() + "/unpause", false,
  13.179                  Collections.singleton(HttpURLConnection.HTTP_NO_CONTENT));
  13.180  
  13.181          if (emitEvents) {
  13.182 @@ -426,7 +428,7 @@
  13.183  
  13.184      public void remove(DockerTag tag) throws DockerException {
  13.185          String id = getImage(tag);
  13.186 -        doDeleteRequest(instance.getUrl(), "/images/" + id, true, REMOVE_IMAGE_CODES);
  13.187 +        doDeleteRequest("/images/" + id, true, REMOVE_IMAGE_CODES);
  13.188  
  13.189          // XXX to be precise we should emit DELETE event if we
  13.190          // delete the last image, but for our purpose this is enough
  13.191 @@ -438,7 +440,7 @@
  13.192      }
  13.193  
  13.194      public void remove(DockerContainer container) throws DockerException {
  13.195 -        doDeleteRequest(instance.getUrl(), "/containers/" + container.getId(), false,
  13.196 +        doDeleteRequest("/containers/" + container.getId(), false,
  13.197                  REMOVE_CONTAINER_CODES);
  13.198  
  13.199          if (emitEvents) {
  13.200 @@ -450,8 +452,8 @@
  13.201  
  13.202      public void resizeTerminal(DockerContainer container, int rows, int columns) throws DockerException {
  13.203          // formally there should be restart so changes take place
  13.204 -        doPostRequest(instance.getUrl(), "/containers/" + container.getId() + "/resize?h=" + rows + "&w=" + columns,
  13.205 -                null, false, Collections.singleton(HttpURLConnection.HTTP_OK));
  13.206 +        doPostRequest("/containers/" + container.getId() + "/resize?h=" + rows + "&w=" + columns,
  13.207 +                false, Collections.singleton(HttpURLConnection.HTTP_OK));
  13.208      }
  13.209  
  13.210      // this call is BLOCKING
  13.211 @@ -459,10 +461,9 @@
  13.212          assert !SwingUtilities.isEventDispatchThread() : "Remote access invoked from EDT";
  13.213  
  13.214          DockerContainerDetail info = DockerAction.this.getDetail(container);
  13.215 -        Socket s = null;
  13.216 +        Endpoint s = null;
  13.217          try {
  13.218 -            URL url = createURL(instance.getUrl(), null);
  13.219 -            s = createSocket(url);
  13.220 +            s = createEndpoint();
  13.221  
  13.222              OutputStream os = s.getOutputStream();
  13.223              os.write(("POST /containers/" + container.getId()
  13.224 @@ -492,10 +493,10 @@
  13.225              Charset ch = HttpUtils.getCharset(response);
  13.226              Integer length = HttpUtils.getLength(response.getHeaders());
  13.227              if (length != null && length <= 0) {
  13.228 -                closeSocket(s);
  13.229 +                closeEndpoint(s);
  13.230                  return new ActionStreamResult(new EmptyStreamResult(info.isTty()));
  13.231              }
  13.232 -            is = HttpUtils.getResponseStream(is, response);
  13.233 +            is = HttpUtils.getResponseStream(is, response, true);
  13.234  
  13.235              if (info.isTty()) {
  13.236                  return new ActionStreamResult(new DirectStreamResult(s, ch, is));
  13.237 @@ -503,13 +504,13 @@
  13.238                  return new ActionStreamResult(new MuxedStreamResult(s, ch, is));
  13.239              }
  13.240          } catch (MalformedURLException e) {
  13.241 -            closeSocket(s);
  13.242 +            closeEndpoint(s);
  13.243              throw new DockerException(e);
  13.244          } catch (IOException e) {
  13.245 -            closeSocket(s);
  13.246 +            closeEndpoint(s);
  13.247              throw new DockerException(e);
  13.248          } catch (DockerException e) {
  13.249 -            closeSocket(s);
  13.250 +            closeEndpoint(s);
  13.251              throw e;
  13.252          }
  13.253      }
  13.254 @@ -520,26 +521,34 @@
  13.255  
  13.256          try {
  13.257              DockerName parsed = DockerName.parse(imageName);
  13.258 -            URL httpUrl = createURL(instance.getUrl(), "/images/create?fromImage="
  13.259 -                    + HttpUtils.encodeParameter(imageName));
  13.260 -            HttpURLConnection conn = createConnection(httpUrl);
  13.261 +            Endpoint s = createEndpoint();
  13.262              try {
  13.263 -                conn.setRequestMethod("POST");
  13.264 +                OutputStream os = s.getOutputStream();
  13.265 +                os.write(("POST /images/create?fromImage="
  13.266 +                    + HttpUtils.encodeParameter(imageName) + " HTTP/1.1\r\n").getBytes("ISO-8859-1"));
  13.267                  Pair<String, String> authHeader = null;
  13.268                  JSONObject auth = createAuthObject(CredentialsManager.getDefault().getCredentials(parsed.getRegistry()));
  13.269                  authHeader = Pair.of("X-Registry-Auth", HttpUtils.encodeBase64(auth.toJSONString()));
  13.270 -                HttpUtils.configureHeaders(conn, DockerConfig.getDefault().getHttpHeaders(),
  13.271 +                HttpUtils.configureHeaders(os, DockerConfig.getDefault().getHttpHeaders(),
  13.272                          ACCEPT_JSON_HEADER, authHeader);
  13.273 +                os.write(("\r\n").getBytes("ISO-8859-1"));
  13.274 +                os.flush();
  13.275  
  13.276 -                if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
  13.277 -                    String error = HttpUtils.readError(conn);
  13.278 -                    throw codeToException(conn.getResponseCode(),
  13.279 -                            error != null ? error : conn.getResponseMessage());
  13.280 +                InputStream is = s.getInputStream();
  13.281 +                HttpUtils.Response response = HttpUtils.readResponse(is);
  13.282 +                int responseCode = response.getCode();
  13.283 +
  13.284 +                is = HttpUtils.getResponseStream(is, response, false);
  13.285 +
  13.286 +                if (responseCode != HttpURLConnection.HTTP_OK) {
  13.287 +                    String error = HttpUtils.readContent(is, response);
  13.288 +                    throw codeToException(responseCode,
  13.289 +                            error != null ? error : response.getMessage());
  13.290                  }
  13.291  
  13.292                  String authFailure = null;
  13.293                  JSONParser parser = new JSONParser();
  13.294 -                try (InputStreamReader r = new InputStreamReader(conn.getInputStream(), HttpUtils.getCharset(conn))) {
  13.295 +                try (InputStreamReader r = new InputStreamReader(is, HttpUtils.getCharset(response))) {
  13.296                      String line;
  13.297                      while ((line = readEventObject(r)) != null) {
  13.298                          JSONObject o = (JSONObject) parser.parse(line);
  13.299 @@ -560,7 +569,7 @@
  13.300                      throw new DockerAuthenticationException(authFailure);
  13.301                  }
  13.302              } finally {
  13.303 -                conn.disconnect();
  13.304 +                closeEndpoint(s);
  13.305              }
  13.306          } catch (MalformedURLException e) {
  13.307              throw new DockerException(e);
  13.308 @@ -589,25 +598,33 @@
  13.309                  action.append("?tag=").append(HttpUtils.encodeParameter(tagString));
  13.310              }
  13.311  
  13.312 -            URL httpUrl = createURL(instance.getUrl(), action.toString());
  13.313 -            HttpURLConnection conn = createConnection(httpUrl);
  13.314 +            Endpoint s = createEndpoint();
  13.315              try {
  13.316 -                conn.setRequestMethod("POST");
  13.317 +                OutputStream os = s.getOutputStream();
  13.318 +                os.write(("POST " + action.toString() + " HTTP/1.1\r\n").getBytes("ISO-8859-1"));
  13.319                  Pair<String, String> authHeader = null;
  13.320                  JSONObject auth = createAuthObject(CredentialsManager.getDefault().getCredentials(parsed.getRegistry()));
  13.321                  authHeader = Pair.of("X-Registry-Auth", HttpUtils.encodeBase64(auth.toJSONString()));
  13.322 -                HttpUtils.configureHeaders(conn, DockerConfig.getDefault().getHttpHeaders(),
  13.323 +                HttpUtils.configureHeaders(os, DockerConfig.getDefault().getHttpHeaders(),
  13.324                          ACCEPT_JSON_HEADER, authHeader);
  13.325 +                os.write(("\r\n").getBytes("ISO-8859-1"));
  13.326 +                os.flush();
  13.327  
  13.328 -                if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
  13.329 -                    String error = HttpUtils.readError(conn);
  13.330 -                    throw codeToException(conn.getResponseCode(),
  13.331 -                            error != null ? error : conn.getResponseMessage());
  13.332 +                InputStream is = s.getInputStream();
  13.333 +                HttpUtils.Response response = HttpUtils.readResponse(is);
  13.334 +                int responseCode = response.getCode();
  13.335 +
  13.336 +                is = HttpUtils.getResponseStream(is, response, false);
  13.337 +
  13.338 +                if (responseCode != HttpURLConnection.HTTP_OK) {
  13.339 +                    String error = HttpUtils.readContent(is, response);
  13.340 +                    throw codeToException(responseCode,
  13.341 +                            error != null ? error : response.getMessage());
  13.342                  }
  13.343  
  13.344                  String authFailure = null;
  13.345                  JSONParser parser = new JSONParser();
  13.346 -                try (InputStreamReader r = new InputStreamReader(conn.getInputStream(), HttpUtils.getCharset(conn))) {
  13.347 +                try (InputStreamReader r = new InputStreamReader(is, HttpUtils.getCharset(response))) {
  13.348                      String line;
  13.349                      while ((line = readEventObject(r)) != null) {
  13.350                          JSONObject o = (JSONObject) parser.parse(line);
  13.351 @@ -628,7 +645,7 @@
  13.352                      throw new DockerAuthenticationException(authFailure);
  13.353                  }
  13.354              } finally {
  13.355 -                conn.disconnect();
  13.356 +                closeEndpoint(s);
  13.357              }
  13.358          } catch (MalformedURLException e) {
  13.359              throw new DockerException(e);
  13.360 @@ -663,15 +680,14 @@
  13.361                      dockerfileName = dockerfile.getName();
  13.362                  }
  13.363  
  13.364 -                Socket s = null;
  13.365 +                Endpoint s = null;
  13.366                  try {
  13.367 -                    URL url = createURL(instance.getUrl(), null);
  13.368 -                    s = createSocket(url);
  13.369 +                    s = createEndpoint();
  13.370                      synchronized (handler) {
  13.371                          if (handler.isCancelled()) {
  13.372                              return null;
  13.373                          }
  13.374 -                        handler.setSocket(s);
  13.375 +                        handler.setEndpoint(s);
  13.376                      }
  13.377  
  13.378                      StringBuilder request = new StringBuilder();
  13.379 @@ -746,7 +762,7 @@
  13.380                          throw new DockerException(ex.getCause());
  13.381                      }
  13.382  
  13.383 -                    is = HttpUtils.getResponseStream(is, response);
  13.384 +                    is = HttpUtils.getResponseStream(is, response, false);
  13.385  
  13.386                      JSONParser parser = new JSONParser();
  13.387                      try (InputStreamReader r = new InputStreamReader(is, HttpUtils.getCharset(response))) {
  13.388 @@ -802,16 +818,16 @@
  13.389                      }
  13.390                      return null;
  13.391                  } catch (MalformedURLException e) {
  13.392 -                    closeSocket(s);
  13.393 +                    closeEndpoint(s);
  13.394                      throw new DockerException(e);
  13.395                  } catch (IOException e) {
  13.396 -                    closeSocket(s);
  13.397 +                    closeEndpoint(s);
  13.398                      if (!handler.isCancelled()) {
  13.399                          throw new DockerException(e);
  13.400                      }
  13.401                      return null;
  13.402                  } catch (DockerException e) {
  13.403 -                    closeSocket(s);
  13.404 +                    closeEndpoint(s);
  13.405                      throw e;
  13.406                  }
  13.407              }
  13.408 @@ -835,10 +851,9 @@
  13.409          assert !SwingUtilities.isEventDispatchThread() : "Remote access invoked from EDT";
  13.410  
  13.411          DockerContainerDetail info = getDetail(container);
  13.412 -        Socket s = null;
  13.413 +        Endpoint s = null;
  13.414          try {
  13.415 -            URL url = createURL(instance.getUrl(), null);
  13.416 -            s = createSocket(url);
  13.417 +            s = createEndpoint();
  13.418  
  13.419              OutputStream os = s.getOutputStream();
  13.420              os.write(("GET /containers/" + container.getId() + "/logs?stderr=1&stdout=1&timestamps=1&follow=1 HTTP/1.1\r\n").getBytes("ISO-8859-1"));
  13.421 @@ -855,7 +870,7 @@
  13.422                          error != null ? error : response.getMessage());
  13.423              }
  13.424  
  13.425 -            is = HttpUtils.getResponseStream(is, response);
  13.426 +            is = HttpUtils.getResponseStream(is, response, true);
  13.427  
  13.428              StreamItem.Fetcher fetcher;
  13.429              Integer length = HttpUtils.getLength(response.getHeaders());
  13.430 @@ -876,23 +891,22 @@
  13.431              }
  13.432              return new ActionChunkedResult(s, fetcher, HttpUtils.getCharset(response));
  13.433          } catch (MalformedURLException e) {
  13.434 -            closeSocket(s);
  13.435 +            closeEndpoint(s);
  13.436              throw new DockerException(e);
  13.437          } catch (IOException e) {
  13.438 -            closeSocket(s);
  13.439 +            closeEndpoint(s);
  13.440              throw new DockerException(e);
  13.441          } catch (DockerException e) {
  13.442 -            closeSocket(s);
  13.443 +            closeEndpoint(s);
  13.444              throw e;
  13.445          }
  13.446      }
  13.447  
  13.448      // this call is BLOCKING
  13.449      public Pair<DockerContainer, ActionStreamResult> run(String name, JSONObject configuration) throws DockerException {
  13.450 -        Socket s = null;
  13.451 +        Endpoint s = null;
  13.452          try {
  13.453 -            URL url = createURL(instance.getUrl(), null);
  13.454 -            s = createSocket(url);
  13.455 +            s = createEndpoint();
  13.456  
  13.457              byte[] data = configuration.toJSONString().getBytes("UTF-8");
  13.458              Map<String, String> defaultHeaders = DockerConfig.getDefault().getHttpHeaders();
  13.459 @@ -914,12 +928,13 @@
  13.460                          error != null ? error : response.getMessage());
  13.461              }
  13.462  
  13.463 -            is = HttpUtils.getResponseStream(is, response);
  13.464 +            // we send a second request to the stream later
  13.465 +            InputStream nis = HttpUtils.getResponseStream(is, response, false);
  13.466  
  13.467              JSONObject value;
  13.468              try {
  13.469                  JSONParser parser = new JSONParser();
  13.470 -                value = (JSONObject) parser.parse(HttpUtils.readContent(is, response));
  13.471 +                value = (JSONObject) parser.parse(HttpUtils.readContent(nis, response));
  13.472              } catch (ParseException ex) {
  13.473                  throw new DockerException(ex);
  13.474              }
  13.475 @@ -947,29 +962,33 @@
  13.476  
  13.477              return Pair.of(container, r);
  13.478          } catch (MalformedURLException e) {
  13.479 -            closeSocket(s);
  13.480 +            closeEndpoint(s);
  13.481              throw new DockerException(e);
  13.482          } catch (IOException e) {
  13.483 -            closeSocket(s);
  13.484 +            closeEndpoint(s);
  13.485              throw new DockerException(e);
  13.486          } catch (DockerException e) {
  13.487 -            closeSocket(s);
  13.488 +            closeEndpoint(s);
  13.489              throw e;
  13.490          }
  13.491      }
  13.492  
  13.493 -    boolean ping() {
  13.494 +    public boolean ping() {
  13.495          assert !SwingUtilities.isEventDispatchThread() : "Remote access invoked from EDT";
  13.496          try {
  13.497 -            URL httpUrl = createURL(instance.getUrl(), "/_ping");
  13.498 -            HttpURLConnection conn = createConnection(httpUrl);
  13.499 +            Endpoint s = createEndpoint();
  13.500              try {
  13.501 -                conn.setRequestMethod("GET");
  13.502 -                return conn.getResponseCode() == HttpURLConnection.HTTP_OK;
  13.503 +                OutputStream os = s.getOutputStream();
  13.504 +                // FIXME should we use default headers ?
  13.505 +                os.write(("GET /_ping HTTP/1.1\r\n\r\n").getBytes("ISO-8859-1"));
  13.506 +                os.flush();
  13.507 +
  13.508 +                InputStream is = s.getInputStream();
  13.509 +                HttpUtils.Response response = HttpUtils.readResponse(is);
  13.510 +                return response.getCode() == HttpURLConnection.HTTP_OK;
  13.511              } finally {
  13.512 -                conn.disconnect();
  13.513 +                closeEndpoint(s);
  13.514              }
  13.515 -
  13.516          } catch (MalformedURLException ex) {
  13.517              LOGGER.log(Level.INFO, null, ex);
  13.518          } catch (IOException ex) {
  13.519 @@ -983,8 +1002,7 @@
  13.520          assert !SwingUtilities.isEventDispatchThread() : "Remote access invoked from EDT";
  13.521  
  13.522          try {
  13.523 -            URL url = createURL(instance.getUrl(), null);
  13.524 -            Socket s = createSocket(url);
  13.525 +            Endpoint s = createEndpoint();
  13.526              try {
  13.527                  OutputStream os = s.getOutputStream();
  13.528                  os.write(("GET " + (since != null ? "/events?since=" + since : "/events") + " HTTP/1.1\r\n"
  13.529 @@ -1001,7 +1019,7 @@
  13.530                              error != null ? error : response.getMessage());
  13.531                  }
  13.532  
  13.533 -                is = HttpUtils.getResponseStream(is, response);
  13.534 +                is = HttpUtils.getResponseStream(is, response, false);
  13.535  
  13.536                  if (connectionListener != null) {
  13.537                      connectionListener.onConnect(s);
  13.538 @@ -1031,7 +1049,7 @@
  13.539                      }
  13.540                  }
  13.541              } finally {
  13.542 -                closeSocket(s);
  13.543 +                closeEndpoint(s);
  13.544              }
  13.545          } catch (MalformedURLException e) {
  13.546              throw new DockerException(e);
  13.547 @@ -1040,33 +1058,40 @@
  13.548         }
  13.549      }
  13.550  
  13.551 -    private Object doGetRequest(@NonNull String url, @NonNull String action, Set<Integer> okCodes) throws DockerException {
  13.552 +    private Object doGetRequest(@NonNull String action, Set<Integer> okCodes) throws DockerException {
  13.553          assert !SwingUtilities.isEventDispatchThread() : "Remote access invoked from EDT";
  13.554  
  13.555          try {
  13.556 -            URL httpUrl = createURL(url, action);
  13.557 -            HttpURLConnection conn = createConnection(httpUrl);
  13.558 +            Endpoint s = createEndpoint();
  13.559              try {
  13.560 -                conn.setRequestMethod("GET");
  13.561 -                HttpUtils.configureHeaders(conn, DockerConfig.getDefault().getHttpHeaders(), ACCEPT_JSON_HEADER);
  13.562 +                OutputStream os = s.getOutputStream();
  13.563 +                os.write(("GET " + action + " HTTP/1.1\r\n").getBytes("ISO-8859-1"));
  13.564 +                HttpUtils.configureHeaders(os, DockerConfig.getDefault().getHttpHeaders(), ACCEPT_JSON_HEADER);
  13.565 +                os.write(("\r\n").getBytes("ISO-8859-1"));
  13.566 +                os.flush();
  13.567  
  13.568 -                if (!okCodes.contains(conn.getResponseCode())) {
  13.569 -                    String error = HttpUtils.readError(conn);
  13.570 -                    throw codeToException(conn.getResponseCode(),
  13.571 -                            error != null ? error : conn.getResponseMessage());
  13.572 +                InputStream is = s.getInputStream();
  13.573 +                HttpUtils.Response response = HttpUtils.readResponse(is);
  13.574 +                int responseCode = response.getCode();
  13.575 +
  13.576 +                if (!okCodes.contains(responseCode)) {
  13.577 +                    String error = HttpUtils.readContent(is, response);
  13.578 +                    throw codeToException(responseCode,
  13.579 +                            error != null ? error : response.getMessage());
  13.580                  }
  13.581  
  13.582 +                is = HttpUtils.getResponseStream(is, response, false);
  13.583 +
  13.584                  try (BufferedReader br = new BufferedReader(new InputStreamReader(
  13.585 -                        (conn.getInputStream())))) {
  13.586 +                        is, HttpUtils.getCharset(response)))) {
  13.587                      JSONParser parser = new JSONParser();
  13.588                      return parser.parse(br);
  13.589                  } catch (ParseException ex) {
  13.590                      throw new DockerException(ex);
  13.591                  }
  13.592              } finally {
  13.593 -                conn.disconnect();
  13.594 +                closeEndpoint(s);
  13.595              }
  13.596 -
  13.597          } catch (MalformedURLException e) {
  13.598              throw new DockerException(e);
  13.599          } catch (IOException e) {
  13.600 @@ -1074,34 +1099,33 @@
  13.601          }
  13.602      }
  13.603  
  13.604 -    private Object doPostRequest(@NonNull String url, @NonNull String action,
  13.605 -            @NullAllowed InputStream data, boolean output, Set<Integer> okCodes) throws DockerException {
  13.606 +    private Object doPostRequest(@NonNull String action, boolean output, Set<Integer> okCodes) throws DockerException {
  13.607          assert !SwingUtilities.isEventDispatchThread() : "Remote access invoked from EDT";
  13.608  
  13.609          try {
  13.610 -            URL httpUrl = createURL(url, action);
  13.611 -            HttpURLConnection conn = createConnection(httpUrl);
  13.612 +            Endpoint s = createEndpoint();
  13.613              try {
  13.614 -                conn.setRequestMethod("POST");
  13.615 -                HttpUtils.configureHeaders(conn, DockerConfig.getDefault().getHttpHeaders(),
  13.616 +                OutputStream os = s.getOutputStream();
  13.617 +                os.write(("POST " + action + " HTTP/1.1\r\n").getBytes("ISO-8859-1"));
  13.618 +                HttpUtils.configureHeaders(os, DockerConfig.getDefault().getHttpHeaders(),
  13.619                          Pair.of("Content-Type", "application/json"));
  13.620 +                os.write(("\r\n").getBytes("ISO-8859-1"));
  13.621 +                os.flush();
  13.622  
  13.623 -                if (data != null) {
  13.624 -                    conn.setDoOutput(true);
  13.625 -                    try (OutputStream os = new BufferedOutputStream(conn.getOutputStream())) {
  13.626 -                        FileUtil.copy(data, os);
  13.627 -                    }
  13.628 -                }
  13.629 +                InputStream is = s.getInputStream();
  13.630 +                HttpUtils.Response response = HttpUtils.readResponse(is);
  13.631 +                int responseCode = response.getCode();
  13.632  
  13.633 -                if (!okCodes.contains(conn.getResponseCode())) {
  13.634 -                    String error = HttpUtils.readError(conn);
  13.635 -                    throw codeToException(conn.getResponseCode(),
  13.636 -                            error != null ? error : conn.getResponseMessage());
  13.637 +                if (!okCodes.contains(responseCode)) {
  13.638 +                    String error = HttpUtils.readContent(is, response);
  13.639 +                    throw codeToException(responseCode,
  13.640 +                            error != null ? error : response.getMessage());
  13.641                  }
  13.642  
  13.643                  if (output) {
  13.644 +                    is = HttpUtils.getResponseStream(is, response, false);
  13.645                      try (BufferedReader br = new BufferedReader(new InputStreamReader(
  13.646 -                            (conn.getInputStream())))) {
  13.647 +                            is, HttpUtils.getCharset(response)))) {
  13.648                          JSONParser parser = new JSONParser();
  13.649                          return parser.parse(br);
  13.650                      } catch (ParseException ex) {
  13.651 @@ -1111,9 +1135,8 @@
  13.652                      return null;
  13.653                  }
  13.654              } finally {
  13.655 -                conn.disconnect();
  13.656 +                closeEndpoint(s);
  13.657              }
  13.658 -
  13.659          } catch (MalformedURLException e) {
  13.660              throw new DockerException(e);
  13.661          } catch (IOException e) {
  13.662 @@ -1121,25 +1144,32 @@
  13.663          }
  13.664      }
  13.665  
  13.666 -    private Object doDeleteRequest(@NonNull String url, @NonNull String action, boolean output, Set<Integer> okCodes) throws DockerException {
  13.667 +    private Object doDeleteRequest(@NonNull String action, boolean output, Set<Integer> okCodes) throws DockerException {
  13.668          assert !SwingUtilities.isEventDispatchThread() : "Remote access invoked from EDT";
  13.669  
  13.670          try {
  13.671 -            URL httpUrl = createURL(url, action);
  13.672 -            HttpURLConnection conn = createConnection(httpUrl);
  13.673 +            Endpoint s = createEndpoint();
  13.674              try {
  13.675 -                conn.setRequestMethod("DELETE");
  13.676 -                HttpUtils.configureHeaders(conn, DockerConfig.getDefault().getHttpHeaders(), ACCEPT_JSON_HEADER);
  13.677 +                OutputStream os = s.getOutputStream();
  13.678 +                os.write(("DELETE " + action + " HTTP/1.1\r\n").getBytes("ISO-8859-1"));
  13.679 +                HttpUtils.configureHeaders(os, DockerConfig.getDefault().getHttpHeaders(), ACCEPT_JSON_HEADER);
  13.680 +                os.write(("\r\n").getBytes("ISO-8859-1"));
  13.681 +                os.flush();
  13.682  
  13.683 -                if (!okCodes.contains(conn.getResponseCode())) {
  13.684 -                    String error = HttpUtils.readError(conn);
  13.685 -                    throw codeToException(conn.getResponseCode(),
  13.686 -                            error != null ? error : conn.getResponseMessage());
  13.687 +                InputStream is = s.getInputStream();
  13.688 +                HttpUtils.Response response = HttpUtils.readResponse(is);
  13.689 +                int responseCode = response.getCode();
  13.690 +
  13.691 +                if (!okCodes.contains(responseCode)) {
  13.692 +                    String error = HttpUtils.readContent(is, response);
  13.693 +                    throw codeToException(responseCode,
  13.694 +                            error != null ? error : response.getMessage());
  13.695                  }
  13.696  
  13.697                  if (output) {
  13.698 +                    is = HttpUtils.getResponseStream(is, response, false);
  13.699                      try (BufferedReader br = new BufferedReader(new InputStreamReader(
  13.700 -                            (conn.getInputStream())))) {
  13.701 +                            is, HttpUtils.getCharset(response)))) {
  13.702                          JSONParser parser = new JSONParser();
  13.703                          return parser.parse(br);
  13.704                      } catch (ParseException ex) {
  13.705 @@ -1149,9 +1179,8 @@
  13.706                      return null;
  13.707                  }
  13.708              } finally {
  13.709 -                conn.disconnect();
  13.710 +                closeEndpoint(s);
  13.711              }
  13.712 -
  13.713          } catch (MalformedURLException e) {
  13.714              throw new DockerException(e);
  13.715          } catch (IOException e) {
  13.716 @@ -1183,57 +1212,37 @@
  13.717          return new StatusEvent(instance, id, status, progress, error, detail);
  13.718      }
  13.719  
  13.720 -    private Socket createSocket(URL url) throws IOException {
  13.721 -        assert "http".equals(url.getProtocol()) || "https".equals(url.getProtocol()); // NOI18N
  13.722 +    private Endpoint createEndpoint() throws IOException {
  13.723 +        String url = instance.getUrl();
  13.724 +        if (url.startsWith("tcp://")) { // NOI18N
  13.725 +            url = "http://" + url.substring(6); // NOI18N
  13.726 +        } else if (url.startsWith("unix://")) { // NOI18N
  13.727 +            url = "file://" + url.substring(7); // NOI18N
  13.728 +        }
  13.729 +        URL realUrl = new URL(url);
  13.730          try {
  13.731 -            if ("https".equals(url.getProtocol())) { // NOI18N
  13.732 +            if ("https".equals(realUrl.getProtocol())) { // NOI18N
  13.733                  SSLContext context = ContextProvider.getInstance().getSSLContext(instance);
  13.734 -                return context.getSocketFactory().createSocket(url.getHost(), url.getPort());
  13.735 +                return Endpoint.forSocket(context.getSocketFactory().createSocket(realUrl.getHost(), realUrl.getPort()));
  13.736 +            } else if ("http".equals(realUrl.getProtocol())) { // NOI18N
  13.737 +                Socket s = new Socket(ProxySelector.getDefault().select(realUrl.toURI()).get(0));
  13.738 +                int port = realUrl.getPort();
  13.739 +                if (port < 0) {
  13.740 +                    port = realUrl.getDefaultPort();
  13.741 +                }
  13.742 +                s.connect(new InetSocketAddress(realUrl.getHost(), port));
  13.743 +                return Endpoint.forSocket(s);
  13.744              } else {
  13.745 -                Socket s = new Socket(ProxySelector.getDefault().select(url.toURI()).get(0));
  13.746 -                int port = url.getPort();
  13.747 -                if (port < 0) {
  13.748 -                    port = url.getDefaultPort();
  13.749 -                }
  13.750 -                s.connect(new InetSocketAddress(url.getHost(), port));
  13.751 -                return s;
  13.752 +                UnixDomainSocketClient s = new UnixDomainSocketClient(
  13.753 +                        new File(realUrl.toURI()).getAbsolutePath(), JUDS.SOCK_STREAM);
  13.754 +                s.setTimeout(0);
  13.755 +                return Endpoint.forDomainSocket(s);
  13.756              }
  13.757          } catch (URISyntaxException ex) {
  13.758              throw new IOException(ex);
  13.759          }
  13.760      }
  13.761  
  13.762 -    private HttpURLConnection createConnection(URL url) throws IOException {
  13.763 -        assert "http".equals(url.getProtocol()) || "https".equals(url.getProtocol()); // NOI18N
  13.764 -        try {
  13.765 -            HttpURLConnection ret = (HttpURLConnection) url.openConnection(ProxySelector.getDefault().select(url.toURI()).get(0));
  13.766 -            if (ret instanceof HttpsURLConnection) {
  13.767 -                ((HttpsURLConnection) ret).setSSLSocketFactory(ContextProvider.getInstance().getSSLContext(instance).getSocketFactory());
  13.768 -            }
  13.769 -            return ret;
  13.770 -        } catch (URISyntaxException ex) {
  13.771 -            throw new IOException(ex);
  13.772 -        }
  13.773 -    }
  13.774 -
  13.775 -    private static URL createURL(@NonNull String url, @NullAllowed String action) throws MalformedURLException, UnsupportedEncodingException {
  13.776 -        // FIXME optimize
  13.777 -        String realUrl;
  13.778 -        if (url.startsWith("tcp://")) {
  13.779 -            realUrl = "http://" + url.substring(6);
  13.780 -        } else {
  13.781 -            realUrl = url;
  13.782 -        }
  13.783 -        if (realUrl.endsWith("/")) {
  13.784 -            realUrl = realUrl.substring(0, realUrl.length() - 1);
  13.785 -        }
  13.786 -        if (action != null) {
  13.787 -            realUrl += action;
  13.788 -        }
  13.789 -
  13.790 -        return new URL(realUrl);
  13.791 -    }
  13.792 -
  13.793      private static JSONObject createAuthObject(Credentials credentials) {
  13.794          JSONObject value = new JSONObject();
  13.795          if (credentials == null) {
  13.796 @@ -1298,7 +1307,7 @@
  13.797          return null;
  13.798      }
  13.799  
  13.800 -    private static void closeSocket(Socket s) {
  13.801 +    private static void closeEndpoint(Endpoint s) {
  13.802          if (s != null) {
  13.803              try {
  13.804                  s.close();
  13.805 @@ -1395,18 +1404,18 @@
  13.806  
  13.807      private static class CancelHandler {
  13.808  
  13.809 -        private Socket socket;
  13.810 +        private Endpoint endpoint;
  13.811  
  13.812          private boolean cancelled;
  13.813  
  13.814 -        public synchronized void setSocket(Socket socket) {
  13.815 -            this.socket = socket;
  13.816 +        public synchronized void setEndpoint(Endpoint endpoint) {
  13.817 +            this.endpoint = endpoint;
  13.818          }
  13.819  
  13.820          public synchronized void cancel() {
  13.821 -            if (socket != null) {
  13.822 -                closeSocket(socket);
  13.823 -                socket = null;
  13.824 +            if (endpoint != null) {
  13.825 +                closeEndpoint(endpoint);
  13.826 +                endpoint = null;
  13.827                  cancelled = true;
  13.828              }
  13.829          }
    14.1 --- a/docker.api/src/org/netbeans/modules/docker/api/DockerInstance.java	Wed Mar 23 21:02:01 2016 +0000
    14.2 +++ b/docker.api/src/org/netbeans/modules/docker/api/DockerInstance.java	Thu Mar 24 15:34:03 2016 +0100
    14.3 @@ -53,7 +53,6 @@
    14.4  import java.util.prefs.PreferenceChangeEvent;
    14.5  import java.util.prefs.PreferenceChangeListener;
    14.6  import java.util.prefs.Preferences;
    14.7 -import javax.swing.SwingUtilities;
    14.8  import javax.swing.event.ChangeListener;
    14.9  import org.netbeans.api.annotations.common.NonNull;
   14.10  import org.netbeans.api.annotations.common.NullAllowed;
   14.11 @@ -88,116 +87,63 @@
   14.12  
   14.13      private final String url;
   14.14  
   14.15 -    private final Preferences prefs;
   14.16 +    // GuardedBy("this")
   14.17 +    private String displayName;
   14.18 +
   14.19 +    // GuardedBy("this")
   14.20 +    private File caCertificate;
   14.21 +
   14.22 +    // GuardedBy("this")
   14.23 +    private File certificate;
   14.24 +
   14.25 +    // GuardedBy("this")
   14.26 +    private File key;
   14.27 +
   14.28 +    // GuardedBy("this")
   14.29 +    private Preferences prefs;
   14.30  
   14.31      private final DockerEventBus eventBus = new DockerEventBus(this);
   14.32  
   14.33 -    private DockerInstance(String url, Preferences prefs) {
   14.34 +    private DockerInstance(String url, String displayName, File caCertificate, File certificate, File key) {
   14.35          this.url = url;
   14.36 -        this.prefs = prefs;
   14.37 +        this.displayName = displayName;
   14.38 +        this.caCertificate = caCertificate;
   14.39 +        this.certificate = certificate;
   14.40 +        this.key = key;
   14.41      }
   14.42  
   14.43      @NonNull
   14.44 -    static DockerInstance create(@NonNull String displayName, @NonNull String url,
   14.45 +    public static DockerInstance getInstance(@NonNull String url, @NullAllowed String displayName,
   14.46              @NullAllowed File caCertificate, @NullAllowed File certificate, @NullAllowed File key) {
   14.47 -        Preferences global = NbPreferences.forModule(DockerInstance.class).node(INSTANCES_KEY);
   14.48  
   14.49 -        // XXX synchronization ?
   14.50 -        Preferences prefs = null;
   14.51 -        int suffix = 0;
   14.52 -        do {
   14.53 -            prefs = global.node(escapeUrl(url) + suffix);
   14.54 -            suffix++;
   14.55 -        } while (prefs.get(URL_KEY, null) != null && suffix < Integer.MAX_VALUE);
   14.56 -
   14.57 -        prefs.put(DISPLAY_NAME_KEY, displayName);
   14.58 -        prefs.put(URL_KEY, url);
   14.59 -        if (caCertificate != null) {
   14.60 -            prefs.put(CA_CERTIFICATE_PATH_KEY, FileUtil.normalizeFile(caCertificate).getAbsolutePath());
   14.61 -        }
   14.62 -        if (certificate != null) {
   14.63 -            prefs.put(CERTIFICATE_PATH_KEY, FileUtil.normalizeFile(certificate).getAbsolutePath());
   14.64 -        }
   14.65 -        if (key != null) {
   14.66 -            prefs.put(KEY_PATH_KEY, FileUtil.normalizeFile(key).getAbsolutePath());
   14.67 -        }
   14.68 -        try {
   14.69 -            prefs.flush();
   14.70 -        } catch (BackingStoreException ex) {
   14.71 -            // XXX better solution?
   14.72 -            throw new IllegalStateException(ex);
   14.73 -        }
   14.74 -        DockerInstance instance = new DockerInstance(url, prefs);
   14.75 -        instance.init();
   14.76 -        return instance;
   14.77 +        return new DockerInstance(url, displayName, caCertificate, certificate, key);
   14.78      }
   14.79  
   14.80 -    static Collection<? extends DockerInstance> findAll() {
   14.81 -        Preferences global = NbPreferences.forModule(DockerInstance.class).node(INSTANCES_KEY);
   14.82 -        assert global != null;
   14.83 -
   14.84 -        List<DockerInstance> instances = new ArrayList<>();
   14.85 -        try {
   14.86 -            String[] names = global.childrenNames();
   14.87 -            if (names.length == 0) {
   14.88 -                LOGGER.log(Level.INFO, "No preferences nodes");
   14.89 -            }
   14.90 -            for (String name : names) {
   14.91 -                Preferences p = global.node(name);
   14.92 -                String displayName = p.get(DISPLAY_NAME_KEY, null);
   14.93 -                String url = p.get(URL_KEY, null);
   14.94 -                if (displayName != null && url != null) {
   14.95 -                    DockerInstance instance = new DockerInstance(url, p);
   14.96 -                    instance.init();
   14.97 -                    instances.add(instance);
   14.98 -                } else {
   14.99 -                    LOGGER.log(Level.INFO, "Invalid Docker instance {0}", name);
  14.100 -                }
  14.101 -            }
  14.102 -            LOGGER.log(Level.FINE, "Loaded {0} Docker instances", instances.size());
  14.103 -        } catch (BackingStoreException ex) {
  14.104 -            LOGGER.log(Level.WARNING, null, ex);
  14.105 -        }
  14.106 -        return instances;
  14.107 +    public String getUrl() {
  14.108 +        return url;
  14.109      }
  14.110  
  14.111      public String getDisplayName() {
  14.112 -        return prefs.get(DISPLAY_NAME_KEY, null);
  14.113 -    }
  14.114 -
  14.115 -    public String getUrl() {
  14.116 -        return prefs.get(URL_KEY, null);
  14.117 +        synchronized (this) {
  14.118 +            return displayName;
  14.119 +        }
  14.120      }
  14.121  
  14.122      public File getCaCertificateFile() {
  14.123 -        String path = prefs.get(CA_CERTIFICATE_PATH_KEY, null);
  14.124 -        return path == null ? null : new File(path);
  14.125 +        synchronized (this) {
  14.126 +            return caCertificate;
  14.127 +        }
  14.128      }
  14.129  
  14.130      public File getCertificateFile() {
  14.131 -        String path = prefs.get(CERTIFICATE_PATH_KEY, null);
  14.132 -        return path == null ? null : new File(path);
  14.133 +        synchronized (this) {
  14.134 +            return certificate;
  14.135 +        }
  14.136      }
  14.137  
  14.138      public File getKeyFile() {
  14.139 -        String path = prefs.get(KEY_PATH_KEY, null);
  14.140 -        return path == null ? null : new File(path);
  14.141 -    }
  14.142 -
  14.143 -    public boolean isAvailable() {
  14.144 -        if (SwingUtilities.isEventDispatchThread()) {
  14.145 -            throw new IllegalStateException("Method isConnected might block the EDT");
  14.146 -        }
  14.147 -        return new DockerAction(this).ping();
  14.148 -    }
  14.149 -
  14.150 -    public void remove() {
  14.151 -        eventBus.close();
  14.152 -        try {
  14.153 -            prefs.removePreferenceChangeListener(listener);
  14.154 -            prefs.removeNode();
  14.155 -        } catch (BackingStoreException ex) {
  14.156 -            LOGGER.log(Level.WARNING, null, ex);
  14.157 +        synchronized (this) {
  14.158 +            return key;
  14.159          }
  14.160      }
  14.161  
  14.162 @@ -263,12 +209,118 @@
  14.163          return "DockerInstance{" + "url=" + url + '}';
  14.164      }
  14.165  
  14.166 -    DockerEventBus getEventBus() {
  14.167 -        return eventBus;
  14.168 +    static Collection<? extends DockerInstance> loadAll() {
  14.169 +        Preferences global = NbPreferences.forModule(DockerInstance.class).node(INSTANCES_KEY);
  14.170 +        assert global != null;
  14.171 +
  14.172 +        List<DockerInstance> instances = new ArrayList<>();
  14.173 +        try {
  14.174 +            String[] names = global.childrenNames();
  14.175 +            if (names.length == 0) {
  14.176 +                LOGGER.log(Level.INFO, "No preferences nodes");
  14.177 +            }
  14.178 +            for (String name : names) {
  14.179 +                Preferences p = global.node(name);
  14.180 +                String displayName = p.get(DISPLAY_NAME_KEY, null);
  14.181 +                String url = p.get(URL_KEY, null);
  14.182 +                if (displayName != null && url != null
  14.183 +                        && (!url.startsWith("file:") || DockerSupport.getDefault().isSocketSupported())) { // NOI18N
  14.184 +                    DockerInstance instance = new DockerInstance(url, null, null, null, null);
  14.185 +                    instance.load(p);
  14.186 +
  14.187 +                    instances.add(instance);
  14.188 +                } else {
  14.189 +                    LOGGER.log(Level.INFO, "Invalid Docker instance {0}", name);
  14.190 +                }
  14.191 +            }
  14.192 +            LOGGER.log(Level.FINE, "Loaded {0} Docker instances", instances.size());
  14.193 +        } catch (BackingStoreException ex) {
  14.194 +            LOGGER.log(Level.WARNING, null, ex);
  14.195 +        }
  14.196 +        return instances;
  14.197      }
  14.198  
  14.199 -    private void init() {
  14.200 -        prefs.addPreferenceChangeListener(listener);
  14.201 +    final void save() {
  14.202 +        synchronized (this) {
  14.203 +            if (prefs != null) {
  14.204 +                throw new IllegalStateException();
  14.205 +            }
  14.206 +
  14.207 +            Preferences global = NbPreferences.forModule(DockerInstance.class).node(INSTANCES_KEY);
  14.208 +
  14.209 +            Preferences p = null;
  14.210 +            int suffix = 0;
  14.211 +            do {
  14.212 +                p = global.node(escapeUrl(url) + suffix);
  14.213 +                suffix++;
  14.214 +            } while (p.get(URL_KEY, null) != null && suffix < Integer.MAX_VALUE);
  14.215 +
  14.216 +            p.put(DISPLAY_NAME_KEY, displayName);
  14.217 +            p.put(URL_KEY, url);
  14.218 +            if (caCertificate != null) {
  14.219 +                p.put(CA_CERTIFICATE_PATH_KEY, FileUtil.normalizeFile(caCertificate).getAbsolutePath());
  14.220 +            }
  14.221 +            if (certificate != null) {
  14.222 +                p.put(CERTIFICATE_PATH_KEY, FileUtil.normalizeFile(certificate).getAbsolutePath());
  14.223 +            }
  14.224 +            if (key != null) {
  14.225 +                p.put(KEY_PATH_KEY, FileUtil.normalizeFile(key).getAbsolutePath());
  14.226 +            }
  14.227 +            try {
  14.228 +                p.flush();
  14.229 +            } catch (BackingStoreException ex) {
  14.230 +                // XXX better solution?
  14.231 +                throw new IllegalStateException(ex);
  14.232 +            }
  14.233 +            this.prefs = p;
  14.234 +            this.prefs.addPreferenceChangeListener(listener);
  14.235 +        }
  14.236 +    }
  14.237 +
  14.238 +    final void delete() {
  14.239 +        synchronized (this) {
  14.240 +            if (prefs == null) {
  14.241 +                throw new IllegalStateException();
  14.242 +            }
  14.243 +            try {
  14.244 +                prefs.removePreferenceChangeListener(listener);
  14.245 +                prefs.removeNode();
  14.246 +            } catch (BackingStoreException ex) {
  14.247 +                LOGGER.log(Level.WARNING, null, ex);
  14.248 +            }
  14.249 +            prefs = null;
  14.250 +        }
  14.251 +    }
  14.252 +
  14.253 +    private final void load(Preferences p) {
  14.254 +        synchronized (this) {
  14.255 +            if (prefs != null) {
  14.256 +                throw new IllegalStateException();
  14.257 +            }
  14.258 +            prefs = p;
  14.259 +            prefs.addPreferenceChangeListener(listener);
  14.260 +            refresh();
  14.261 +        }
  14.262 +    }
  14.263 +
  14.264 +    private void refresh() {
  14.265 +        synchronized (this) {
  14.266 +            if (prefs == null) {
  14.267 +                return;
  14.268 +            }
  14.269 +
  14.270 +            displayName = prefs.get(DISPLAY_NAME_KEY, null);
  14.271 +            String caCertPath = prefs.get(CA_CERTIFICATE_PATH_KEY, null);
  14.272 +            caCertificate = caCertPath == null ? null : new File(caCertPath);
  14.273 +            String certPath = prefs.get(CERTIFICATE_PATH_KEY, null);
  14.274 +            certificate = certPath == null ? null : new File(certPath);
  14.275 +            String keyPath = prefs.get(KEY_PATH_KEY, null);
  14.276 +            key = keyPath == null ? null : new File(keyPath);
  14.277 +        }
  14.278 +    }
  14.279 +
  14.280 +    final DockerEventBus getEventBus() {
  14.281 +        return eventBus;
  14.282      }
  14.283  
  14.284      private static String escapeUrl(String url) {
  14.285 @@ -286,6 +338,7 @@
  14.286  
  14.287          @Override
  14.288          public void preferenceChange(PreferenceChangeEvent evt) {
  14.289 +            refresh();
  14.290              changeSupport.fireChange();
  14.291          }
  14.292      }
    15.1 --- a/docker.api/src/org/netbeans/modules/docker/api/DockerIntegration.java	Wed Mar 23 21:02:01 2016 +0000
    15.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.3 @@ -1,163 +0,0 @@
    15.4 -/*
    15.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    15.6 - *
    15.7 - * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
    15.8 - *
    15.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   15.10 - * Other names may be trademarks of their respective owners.
   15.11 - *
   15.12 - * The contents of this file are subject to the terms of either the GNU
   15.13 - * General Public License Version 2 only ("GPL") or the Common
   15.14 - * Development and Distribution License("CDDL") (collectively, the
   15.15 - * "License"). You may not use this file except in compliance with the
   15.16 - * License. You can obtain a copy of the License at
   15.17 - * http://www.netbeans.org/cddl-gplv2.html
   15.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   15.19 - * specific language governing permissions and limitations under the
   15.20 - * License.  When distributing the software, include this License Header
   15.21 - * Notice in each file and include the License file at
   15.22 - * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   15.23 - * particular file as subject to the "Classpath" exception as provided
   15.24 - * by Oracle in the GPL Version 2 section of the License file that
   15.25 - * accompanied this code. If applicable, add the following below the
   15.26 - * License Header, with the fields enclosed by brackets [] replaced by
   15.27 - * your own identifying information:
   15.28 - * "Portions Copyrighted [year] [name of copyright owner]"
   15.29 - *
   15.30 - * If you wish your version of this file to be governed by only the CDDL
   15.31 - * or only the GPL Version 2, indicate your decision by adding
   15.32 - * "[Contributor] elects to include this software in this distribution
   15.33 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
   15.34 - * single choice of license, a recipient has the option to distribute
   15.35 - * your version of this file under either the CDDL, the GPL Version 2 or
   15.36 - * to extend the choice of license to its licensees as provided above.
   15.37 - * However, if you add GPL Version 2 code and therefore, elected the GPL
   15.38 - * Version 2 license, then the option applies only if the new code is
   15.39 - * made subject to such option by the copyright holder.
   15.40 - *
   15.41 - * Contributor(s):
   15.42 - *
   15.43 - * Portions Copyrighted 2007 Sun Microsystems, Inc.
   15.44 - */
   15.45 -
   15.46 -package org.netbeans.modules.docker.api;
   15.47 -
   15.48 -import java.io.File;
   15.49 -import java.util.Collection;
   15.50 -import java.util.HashMap;
   15.51 -import java.util.HashSet;
   15.52 -import java.util.Map;
   15.53 -import java.util.Set;
   15.54 -import java.util.logging.Logger;
   15.55 -import java.util.prefs.NodeChangeEvent;
   15.56 -import java.util.prefs.NodeChangeListener;
   15.57 -import java.util.prefs.Preferences;
   15.58 -import javax.swing.event.ChangeListener;
   15.59 -import org.netbeans.api.annotations.common.NonNull;
   15.60 -import org.netbeans.api.annotations.common.NullAllowed;
   15.61 -import org.openide.util.ChangeSupport;
   15.62 -import org.openide.util.NbPreferences;
   15.63 -import org.openide.util.Parameters;
   15.64 -
   15.65 -/**
   15.66 - *
   15.67 - * @author Petr Hejl
   15.68 - */
   15.69 -public final class DockerIntegration {
   15.70 -
   15.71 -    private static final Logger LOGGER = Logger.getLogger(DockerIntegration.class.getName());
   15.72 -
   15.73 -    private static DockerIntegration registry;
   15.74 -
   15.75 -    private final ChangeSupport changeSupport = new ChangeSupport(this);
   15.76 -
   15.77 -    // GuardedBy("this")
   15.78 -    private final Map<String, DockerInstance> instances = new HashMap<>();
   15.79 -
   15.80 -    // GuardedBy("this")
   15.81 -    private boolean initialized;
   15.82 -
   15.83 -    private DockerIntegration() {
   15.84 -        super();
   15.85 -    }
   15.86 -
   15.87 -    public static DockerIntegration getDefault() {
   15.88 -        DockerIntegration ret;
   15.89 -        synchronized (DockerIntegration.class) {
   15.90 -            if (registry == null) {
   15.91 -                registry = new DockerIntegration();
   15.92 -                Preferences p = NbPreferences.forModule(DockerInstance.class).node(DockerInstance.INSTANCES_KEY);
   15.93 -                p.addNodeChangeListener(new NodeChangeListener() {
   15.94 -                    @Override
   15.95 -                    public void childAdded(NodeChangeEvent evt) {
   15.96 -                        registry.refresh();
   15.97 -                    }
   15.98 -
   15.99 -                    @Override
  15.100 -                    public void childRemoved(NodeChangeEvent evt) {
  15.101 -                        registry.refresh();
  15.102 -                    }
  15.103 -                });
  15.104 -            }
  15.105 -            ret = registry;
  15.106 -        }
  15.107 -        synchronized (ret) {
  15.108 -            if (!ret.initialized) {
  15.109 -                ret.refresh();
  15.110 -            }
  15.111 -        }
  15.112 -        return ret;
  15.113 -    }
  15.114 -
  15.115 -    public DockerInstance createInstance(@NonNull String displayName, @NonNull String url,
  15.116 -            @NullAllowed File caCertificate, @NullAllowed File certificate, @NullAllowed File key) {
  15.117 -        Parameters.notNull("displayName", displayName);
  15.118 -        Parameters.notNull("url", url);
  15.119 -
  15.120 -        DockerInstance instance;
  15.121 -        synchronized (this) {
  15.122 -            if (instances.containsKey(url)) {
  15.123 -                throw new IllegalStateException("Docker instance already exist: " + url);
  15.124 -            }
  15.125 -            instance = DockerInstance.create(displayName, url, caCertificate, certificate, key);
  15.126 -            instances.put(url, instance);
  15.127 -        }
  15.128 -        changeSupport.fireChange();
  15.129 -        return instance;
  15.130 -    }
  15.131 -
  15.132 -    public Collection<? extends DockerInstance> getInstances() {
  15.133 -        synchronized (this) {
  15.134 -            return new HashSet<>(instances.values());
  15.135 -        }
  15.136 -    }
  15.137 -
  15.138 -    public void addChangeListener(ChangeListener listener) {
  15.139 -        changeSupport.addChangeListener(listener);
  15.140 -    }
  15.141 -
  15.142 -    public void removeChangeListener(ChangeListener listener) {
  15.143 -        changeSupport.removeChangeListener(listener);
  15.144 -    }
  15.145 -
  15.146 -    private void refresh() {
  15.147 -        boolean fire = false;
  15.148 -        synchronized (this) {
  15.149 -            initialized = true;
  15.150 -            Set<String> toRemove = new HashSet<>(instances.keySet());
  15.151 -            for (DockerInstance i : DockerInstance.findAll()) {
  15.152 -                if (instances.get(i.getUrl()) == null) {
  15.153 -                    fire = true;
  15.154 -                    instances.put(i.getUrl(), i);
  15.155 -                }
  15.156 -                toRemove.remove(i.getUrl());
  15.157 -            }
  15.158 -            if (instances.keySet().removeAll(toRemove)) {
  15.159 -                fire = true;
  15.160 -            }
  15.161 -        }
  15.162 -        if (fire) {
  15.163 -            changeSupport.fireChange();
  15.164 -        }
  15.165 -    }
  15.166 -}
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/docker.api/src/org/netbeans/modules/docker/api/DockerSupport.java	Thu Mar 24 15:34:03 2016 +0100
    16.3 @@ -0,0 +1,188 @@
    16.4 +/*
    16.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    16.6 + *
    16.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
    16.8 + *
    16.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   16.10 + * Other names may be trademarks of their respective owners.
   16.11 + *
   16.12 + * The contents of this file are subject to the terms of either the GNU
   16.13 + * General Public License Version 2 only ("GPL") or the Common
   16.14 + * Development and Distribution License("CDDL") (collectively, the
   16.15 + * "License"). You may not use this file except in compliance with the
   16.16 + * License. You can obtain a copy of the License at
   16.17 + * http://www.netbeans.org/cddl-gplv2.html
   16.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   16.19 + * specific language governing permissions and limitations under the
   16.20 + * License.  When distributing the software, include this License Header
   16.21 + * Notice in each file and include the License file at
   16.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   16.23 + * particular file as subject to the "Classpath" exception as provided
   16.24 + * by Oracle in the GPL Version 2 section of the License file that
   16.25 + * accompanied this code. If applicable, add the following below the
   16.26 + * License Header, with the fields enclosed by brackets [] replaced by
   16.27 + * your own identifying information:
   16.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   16.29 + *
   16.30 + * If you wish your version of this file to be governed by only the CDDL
   16.31 + * or only the GPL Version 2, indicate your decision by adding
   16.32 + * "[Contributor] elects to include this software in this distribution
   16.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   16.34 + * single choice of license, a recipient has the option to distribute
   16.35 + * your version of this file under either the CDDL, the GPL Version 2 or
   16.36 + * to extend the choice of license to its licensees as provided above.
   16.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   16.38 + * Version 2 license, then the option applies only if the new code is
   16.39 + * made subject to such option by the copyright holder.
   16.40 + *
   16.41 + * Contributor(s):
   16.42 + *
   16.43 + * Portions Copyrighted 2007 Sun Microsystems, Inc.
   16.44 + */
   16.45 +
   16.46 +package org.netbeans.modules.docker.api;
   16.47 +
   16.48 +import java.util.Collection;
   16.49 +import java.util.HashMap;
   16.50 +import java.util.HashSet;
   16.51 +import java.util.Map;
   16.52 +import java.util.Set;
   16.53 +import java.util.logging.Level;
   16.54 +import java.util.logging.Logger;
   16.55 +import java.util.prefs.NodeChangeEvent;
   16.56 +import java.util.prefs.NodeChangeListener;
   16.57 +import java.util.prefs.Preferences;
   16.58 +import javax.swing.event.ChangeListener;
   16.59 +import org.netbeans.api.annotations.common.NonNull;
   16.60 +import org.openide.util.BaseUtilities;
   16.61 +import org.openide.util.ChangeSupport;
   16.62 +import org.openide.util.NbPreferences;
   16.63 +import org.openide.util.Parameters;
   16.64 +
   16.65 +/**
   16.66 + *
   16.67 + * @author Petr Hejl
   16.68 + */
   16.69 +public final class DockerSupport {
   16.70 +
   16.71 +    private static final Logger LOGGER = Logger.getLogger(DockerSupport.class.getName());
   16.72 +
   16.73 +    private static DockerSupport support;
   16.74 +
   16.75 +    private final ChangeSupport changeSupport = new ChangeSupport(this);
   16.76 +
   16.77 +    // GuardedBy("this")
   16.78 +    private final Map<String, DockerInstance> instances = new HashMap<>();
   16.79 +
   16.80 +    // GuardedBy("this")
   16.81 +    private boolean initialized;
   16.82 +
   16.83 +    private DockerSupport() {
   16.84 +        super();
   16.85 +    }
   16.86 +
   16.87 +    public static DockerSupport getDefault() {
   16.88 +        DockerSupport ret;
   16.89 +        synchronized (DockerSupport.class) {
   16.90 +            if (support == null) {
   16.91 +                support = new DockerSupport();
   16.92 +                Preferences p = NbPreferences.forModule(DockerInstance.class).node(DockerInstance.INSTANCES_KEY);
   16.93 +                p.addNodeChangeListener(new NodeChangeListener() {
   16.94 +                    @Override
   16.95 +                    public void childAdded(NodeChangeEvent evt) {
   16.96 +                        support.refresh();
   16.97 +                    }
   16.98 +
   16.99 +                    @Override
  16.100 +                    public void childRemoved(NodeChangeEvent evt) {
  16.101 +                        support.refresh();
  16.102 +                    }
  16.103 +                });
  16.104 +            }
  16.105 +            ret = support;
  16.106 +        }
  16.107 +        synchronized (ret) {
  16.108 +            if (!ret.isInitialized()) {
  16.109 +                ret.refresh();
  16.110 +            }
  16.111 +        }
  16.112 +        return ret;
  16.113 +    }
  16.114 +
  16.115 +    public DockerInstance addInstance(@NonNull DockerInstance instance) {
  16.116 +        Parameters.notNull("instance", instance);
  16.117 +
  16.118 +        String url = instance.getUrl();
  16.119 +        synchronized (this) {
  16.120 +            if (instances.containsKey(url)) {
  16.121 +                throw new IllegalStateException("Docker instance already exist: " + url);
  16.122 +            }
  16.123 +            instance.save();
  16.124 +            instances.put(url, instance);
  16.125 +        }
  16.126 +        changeSupport.fireChange();
  16.127 +        return instance;
  16.128 +    }
  16.129 +
  16.130 +    public void removeInstance(@NonNull DockerInstance instance) {
  16.131 +        Parameters.notNull("instance", instance);
  16.132 +
  16.133 +        synchronized (this) {
  16.134 +            instances.remove(instance.getUrl());
  16.135 +            instance.delete();
  16.136 +
  16.137 +            // we shouldn't need it and use it
  16.138 +            //instance.getEventBus().close();
  16.139 +        }
  16.140 +        changeSupport.fireChange();
  16.141 +    }
  16.142 +
  16.143 +    public Collection<? extends DockerInstance> getInstances() {
  16.144 +        synchronized (this) {
  16.145 +            return new HashSet<>(instances.values());
  16.146 +        }
  16.147 +    }
  16.148 +
  16.149 +    public void addChangeListener(ChangeListener listener) {
  16.150 +        changeSupport.addChangeListener(listener);
  16.151 +    }
  16.152 +
  16.153 +    public void removeChangeListener(ChangeListener listener) {
  16.154 +        changeSupport.removeChangeListener(listener);
  16.155 +    }
  16.156 +
  16.157 +    public boolean isSocketSupported() {
  16.158 +        if (BaseUtilities.getOperatingSystem() != BaseUtilities.OS_LINUX) {
  16.159 +            return false;
  16.160 +        }
  16.161 +        String arch = System.getProperty("os.arch"); // NOI18N
  16.162 +        return arch != null && (arch.contains("x86") || arch.contains("amd64")); // NOI18N
  16.163 +    }
  16.164 +
  16.165 +    private boolean isInitialized() {
  16.166 +        synchronized (this) {
  16.167 +            return initialized;
  16.168 +        }
  16.169 +    }
  16.170 +
  16.171 +    private void refresh() {
  16.172 +        boolean fire = false;
  16.173 +        synchronized (this) {
  16.174 +            initialized = true;
  16.175 +            Set<String> toRemove = new HashSet<>(instances.keySet());
  16.176 +            for (DockerInstance i : DockerInstance.loadAll()) {
  16.177 +                if (instances.get(i.getUrl()) == null) {
  16.178 +                    fire = true;
  16.179 +                    instances.put(i.getUrl(), i);
  16.180 +                }
  16.181 +                toRemove.remove(i.getUrl());
  16.182 +            }
  16.183 +            if (instances.keySet().removeAll(toRemove)) {
  16.184 +                fire = true;
  16.185 +            }
  16.186 +        }
  16.187 +        if (fire) {
  16.188 +            changeSupport.fireChange();
  16.189 +        }
  16.190 +    }
  16.191 +}
    17.1 --- a/docker.ui/nbproject/project.xml	Wed Mar 23 21:02:01 2016 +0000
    17.2 +++ b/docker.ui/nbproject/project.xml	Thu Mar 24 15:34:03 2016 +0100
    17.3 @@ -115,7 +115,7 @@
    17.4                      <compile-dependency/>
    17.5                      <run-dependency>
    17.6                          <release-version>0-1</release-version>
    17.7 -                        <specification-version>1.15</specification-version>
    17.8 +                        <specification-version>1.19</specification-version>
    17.9                      </run-dependency>
   17.10                  </dependency>
   17.11                  <dependency>
   17.12 @@ -180,7 +180,7 @@
   17.13                      <build-prerequisite/>
   17.14                      <compile-dependency/>
   17.15                      <run-dependency>
   17.16 -                        <specification-version>7.9</specification-version>
   17.17 +                        <specification-version>7.45</specification-version>
   17.18                      </run-dependency>
   17.19                  </dependency>
   17.20                  <dependency>
    18.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/build2/BuildImageAction.java	Wed Mar 23 21:02:01 2016 +0000
    18.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/build2/BuildImageAction.java	Thu Mar 24 15:34:03 2016 +0100
    18.3 @@ -42,7 +42,7 @@
    18.4  package org.netbeans.modules.docker.ui.build2;
    18.5  
    18.6  import org.netbeans.modules.docker.api.DockerInstance;
    18.7 -import org.netbeans.modules.docker.ui.node.EnhancedDockerInstance;
    18.8 +import org.netbeans.modules.docker.ui.node.StatefulDockerInstance;
    18.9  import org.openide.nodes.Node;
   18.10  import org.openide.util.HelpCtx;
   18.11  import org.openide.util.NbBundle;
   18.12 @@ -66,7 +66,7 @@
   18.13          if (activatedNodes.length != 1) {
   18.14              return false;
   18.15          }
   18.16 -        EnhancedDockerInstance checked = activatedNodes[0].getLookup().lookup(EnhancedDockerInstance.class);
   18.17 +        StatefulDockerInstance checked = activatedNodes[0].getLookup().lookup(StatefulDockerInstance.class);
   18.18          if (checked == null || !checked.isAvailable()) {
   18.19              return false;
   18.20          }
    19.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/build2/BuildImageWizard.java	Wed Mar 23 21:02:01 2016 +0000
    19.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/build2/BuildImageWizard.java	Thu Mar 24 15:34:03 2016 +0100
    19.3 @@ -58,7 +58,7 @@
    19.4  import org.netbeans.modules.docker.api.DockerInstance;
    19.5  import org.netbeans.modules.docker.api.DockerAction;
    19.6  import org.netbeans.modules.docker.api.DockerImage;
    19.7 -import org.netbeans.modules.docker.api.DockerIntegration;
    19.8 +import org.netbeans.modules.docker.api.DockerSupport;
    19.9  import org.netbeans.modules.docker.ui.build2.InputOutputCache.CachedInputOutput;
   19.10  import org.openide.DialogDisplayer;
   19.11  import org.openide.WizardDescriptor;
   19.12 @@ -312,7 +312,7 @@
   19.13              this.listener = new ActionStateListener(this, buildTask.getInstance(), fo);
   19.14  
   19.15              fo.addFileChangeListener(listener);
   19.16 -            DockerIntegration.getDefault().addChangeListener(listener);
   19.17 +            DockerSupport.getDefault().addChangeListener(listener);
   19.18  
   19.19              listener.refresh();
   19.20          }
   19.21 @@ -325,7 +325,7 @@
   19.22              FileObject fo = listener.getFileObject();
   19.23  
   19.24              fo.removeFileChangeListener(listener);
   19.25 -            DockerIntegration.getDefault().removeChangeListener(listener);
   19.26 +            DockerSupport.getDefault().removeChangeListener(listener);
   19.27  
   19.28              buildTask = null;
   19.29              listener = null;
   19.30 @@ -428,7 +428,7 @@
   19.31                  action.detach();
   19.32              }
   19.33              DockerInstance inst = this.instance.get();
   19.34 -            DockerIntegration integration = DockerIntegration.getDefault();
   19.35 +            DockerSupport integration = DockerSupport.getDefault();
   19.36              if (inst == null || !integration.getInstances().contains(inst)) {
   19.37                  action.detach();
   19.38              }
    20.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/build2/BuildInstanceVisual.java	Wed Mar 23 21:02:01 2016 +0000
    20.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/build2/BuildInstanceVisual.java	Thu Mar 24 15:34:03 2016 +0100
    20.3 @@ -52,7 +52,7 @@
    20.4  import javax.swing.event.ChangeEvent;
    20.5  import javax.swing.event.ChangeListener;
    20.6  import org.netbeans.modules.docker.api.DockerInstance;
    20.7 -import org.netbeans.modules.docker.api.DockerIntegration;
    20.8 +import org.netbeans.modules.docker.api.DockerSupport;
    20.9  import org.netbeans.modules.docker.ui.UiUtils;
   20.10  import org.netbeans.modules.docker.ui.wizard.AddDockerInstanceWizard;
   20.11  import org.openide.util.ChangeSupport;
   20.12 @@ -91,7 +91,7 @@
   20.13      public BuildInstanceVisual() {
   20.14          initComponents();
   20.15  
   20.16 -        DockerIntegration integration = DockerIntegration.getDefault();
   20.17 +        DockerSupport integration = DockerSupport.getDefault();
   20.18          integration.addChangeListener(WeakListeners.change(instancesListener, integration));
   20.19  
   20.20          instanceComboBox.setModel(model);
   20.21 @@ -144,7 +144,7 @@
   20.22          assert SwingUtilities.isEventDispatchThread();
   20.23          DockerInstanceWrapper wrapper = (DockerInstanceWrapper) model.getSelectedItem();
   20.24          model.removeAllElements();
   20.25 -        List<DockerInstance> instances = new ArrayList<>(DockerIntegration.getDefault().getInstances());
   20.26 +        List<DockerInstance> instances = new ArrayList<>(DockerSupport.getDefault().getInstances());
   20.27          Collections.sort(instances, UiUtils.getInstanceComparator());
   20.28          for (DockerInstance i : instances) {
   20.29              model.addElement(new DockerInstanceWrapper(i));
    21.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/node/AbstractContainerAction.java	Wed Mar 23 21:02:01 2016 +0000
    21.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/node/AbstractContainerAction.java	Thu Mar 24 15:34:03 2016 +0100
    21.3 @@ -82,7 +82,7 @@
    21.4      @Override
    21.5      protected final void performAction(Node[] activatedNodes) {
    21.6          for (final Node node : activatedNodes) {
    21.7 -            final EnhancedDockerContainer container = node.getLookup().lookup(EnhancedDockerContainer.class);
    21.8 +            final StatefulDockerContainer container = node.getLookup().lookup(StatefulDockerContainer.class);
    21.9              if (container != null) {
   21.10                  final ProgressHandle handle = ProgressHandle.createHandle(getProgressMessage(container.getContainer()));
   21.11                  handle.start();
   21.12 @@ -106,7 +106,7 @@
   21.13      @Override
   21.14      protected final boolean enable(Node[] activatedNodes) {
   21.15          for (Node node : activatedNodes) {
   21.16 -            EnhancedDockerContainer container = node.getLookup().lookup(EnhancedDockerContainer.class);
   21.17 +            StatefulDockerContainer container = node.getLookup().lookup(StatefulDockerContainer.class);
   21.18              if (container == null || !isEnabled(container.getDetail())) {
   21.19                  return false;
   21.20              }
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/node/DockerChildFactory.java	Thu Mar 24 15:34:03 2016 +0100
    22.3 @@ -0,0 +1,116 @@
    22.4 +/*
    22.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    22.6 + *
    22.7 + * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
    22.8 + *
    22.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   22.10 + * Other names may be trademarks of their respective owners.
   22.11 + *
   22.12 + * The contents of this file are subject to the terms of either the GNU
   22.13 + * General Public License Version 2 only ("GPL") or the Common
   22.14 + * Development and Distribution License("CDDL") (collectively, the
   22.15 + * "License"). You may not use this file except in compliance with the
   22.16 + * License. You can obtain a copy of the License at
   22.17 + * http://www.netbeans.org/cddl-gplv2.html
   22.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   22.19 + * specific language governing permissions and limitations under the
   22.20 + * License.  When distributing the software, include this License Header
   22.21 + * Notice in each file and include the License file at
   22.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   22.23 + * particular file as subject to the "Classpath" exception as provided
   22.24 + * by Oracle in the GPL Version 2 section of the License file that
   22.25 + * accompanied this code. If applicable, add the following below the
   22.26 + * License Header, with the fields enclosed by brackets [] replaced by
   22.27 + * your own identifying information:
   22.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   22.29 + *
   22.30 + * If you wish your version of this file to be governed by only the CDDL
   22.31 + * or only the GPL Version 2, indicate your decision by adding
   22.32 + * "[Contributor] elects to include this software in this distribution
   22.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   22.34 + * single choice of license, a recipient has the option to distribute
   22.35 + * your version of this file under either the CDDL, the GPL Version 2 or
   22.36 + * to extend the choice of license to its licensees as provided above.
   22.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   22.38 + * Version 2 license, then the option applies only if the new code is
   22.39 + * made subject to such option by the copyright holder.
   22.40 + *
   22.41 + * Contributor(s):
   22.42 + *
   22.43 + * Portions Copyrighted 2016 Sun Microsystems, Inc.
   22.44 + */
   22.45 +package org.netbeans.modules.docker.ui.node;
   22.46 +
   22.47 +import java.util.ArrayList;
   22.48 +import java.util.Collections;
   22.49 +import java.util.List;
   22.50 +import javax.swing.event.ChangeEvent;
   22.51 +import javax.swing.event.ChangeListener;
   22.52 +import org.netbeans.modules.docker.api.DockerInstance;
   22.53 +import org.netbeans.modules.docker.api.DockerSupport;
   22.54 +import org.netbeans.modules.docker.ui.UiUtils;
   22.55 +import org.openide.nodes.Node;
   22.56 +import org.openide.util.RequestProcessor;
   22.57 +import org.openide.util.WeakListeners;
   22.58 +
   22.59 +/**
   22.60 + *
   22.61 + * @author Petr Hejl
   22.62 + */
   22.63 +public class DockerChildFactory extends NodeClosingFactory<StatefulDockerInstance> implements ChangeListener {
   22.64 +
   22.65 +    private static final RequestProcessor REFRESH_PROCESSOR = new RequestProcessor("Docker node update/refresh", 5);
   22.66 +
   22.67 +    private final DockerSupport registry;
   22.68 +
   22.69 +    public DockerChildFactory(DockerSupport registry) {
   22.70 +        this.registry = registry;
   22.71 +    }
   22.72 +
   22.73 +    public void init() {
   22.74 +        REFRESH_PROCESSOR.post(new Runnable() {
   22.75 +
   22.76 +            @Override
   22.77 +            public void run() {
   22.78 +                synchronized (DockerChildFactory.this) {
   22.79 +                    registry.addChangeListener(
   22.80 +                            WeakListeners.create(ChangeListener.class, DockerChildFactory.this, registry));
   22.81 +                    updateState(new ChangeEvent(registry));
   22.82 +                }
   22.83 +            }
   22.84 +        });
   22.85 +    }
   22.86 +
   22.87 +    @Override
   22.88 +    public void stateChanged(final ChangeEvent e) {
   22.89 +        REFRESH_PROCESSOR.post(new Runnable() {
   22.90 +            public void run() {
   22.91 +                updateState(e);
   22.92 +            }
   22.93 +        });
   22.94 +    }
   22.95 +
   22.96 +    private synchronized void updateState(final ChangeEvent e) {
   22.97 +        refresh();
   22.98 +    }
   22.99 +
  22.100 +    protected final void refresh() {
  22.101 +        refresh(false);
  22.102 +    }
  22.103 +
  22.104 +    @Override
  22.105 +    protected Node createNodeForKey(StatefulDockerInstance key) {
  22.106 +        return new DockerInstanceNode(key, new DockerInstanceChildFactory(key));
  22.107 +    }
  22.108 +
  22.109 +    @Override
  22.110 +    protected boolean createKeys(List<StatefulDockerInstance> toPopulate) {
  22.111 +        List<DockerInstance> fresh = new ArrayList<>(registry.getInstances());
  22.112 +        Collections.sort(fresh, UiUtils.getInstanceComparator());
  22.113 +        for (DockerInstance i : fresh) {
  22.114 +            toPopulate.add(new StatefulDockerInstance(i));
  22.115 +        }
  22.116 +        return true;
  22.117 +    }
  22.118 +
  22.119 +}
    23.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/node/DockerContainerNode.java	Wed Mar 23 21:02:01 2016 +0000
    23.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/node/DockerContainerNode.java	Thu Mar 24 15:34:03 2016 +0100
    23.3 @@ -71,7 +71,7 @@
    23.4      private static final String RUNNING_ICON
    23.5              = "org/netbeans/modules/docker/ui/resources/badge_running.png"; // NOI18N
    23.6  
    23.7 -    private final EnhancedDockerContainer container;
    23.8 +    private final StatefulDockerContainer container;
    23.9  
   23.10      private final ChangeListener listener = new ChangeListener() {
   23.11          @Override
   23.12 @@ -81,7 +81,7 @@
   23.13          }
   23.14      };
   23.15  
   23.16 -    public DockerContainerNode(EnhancedDockerContainer container) {
   23.17 +    public DockerContainerNode(StatefulDockerContainer container) {
   23.18          super(Children.LEAF, Lookups.fixed(container.getContainer(), container));
   23.19          this.container = container;
   23.20          DockerContainer dockerContainer = container.getContainer();
    24.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/node/DockerContainersChildFactory.java	Wed Mar 23 21:02:01 2016 +0000
    24.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/node/DockerContainersChildFactory.java	Thu Mar 24 15:34:03 2016 +0100
    24.3 @@ -41,6 +41,7 @@
    24.4   */
    24.5  package org.netbeans.modules.docker.ui.node;
    24.6  
    24.7 +import java.io.Closeable;
    24.8  import java.lang.ref.WeakReference;
    24.9  import java.util.ArrayList;
   24.10  import java.util.Collections;
   24.11 @@ -64,7 +65,7 @@
   24.12   *
   24.13   * @author Petr Hejl
   24.14   */
   24.15 -public class DockerContainersChildFactory extends ChildFactory<EnhancedDockerContainer> implements Refreshable {
   24.16 +public class DockerContainersChildFactory extends NodeClosingFactory<StatefulDockerContainer> implements Refreshable, Closeable {
   24.17  
   24.18      private static final Logger LOGGER = Logger.getLogger(DockerContainersChildFactory.class.getName());
   24.19  
   24.20 @@ -72,17 +73,19 @@
   24.21  
   24.22          @Override
   24.23          public int compare(DockerContainer o1, DockerContainer o2) {
   24.24 -            return o1.getId().compareTo(o2.getId());
   24.25 +            return o1.getImage().compareTo(o2.getImage());
   24.26          }
   24.27      };
   24.28  
   24.29      private static final Set<DockerEvent.Status> CHANGE_EVENTS = new HashSet<>();
   24.30  
   24.31      static {
   24.32 -        Collections.addAll(CHANGE_EVENTS, DockerEvent.Status.COPY, DockerEvent.Status.CREATE, DockerEvent.Status.DESTROY);
   24.33 +        // rename is here because it may reorder nodes
   24.34 +        Collections.addAll(CHANGE_EVENTS, DockerEvent.Status.COPY, DockerEvent.Status.CREATE,
   24.35 +                DockerEvent.Status.DESTROY, DockerEvent.Status.RENAME);
   24.36      }
   24.37  
   24.38 -    private final Map<DockerContainer, WeakReference<EnhancedDockerContainer>> cache = new WeakHashMap<>();
   24.39 +    private final Map<DockerContainer, WeakReference<StatefulDockerContainer>> cache = new WeakHashMap<>();
   24.40  
   24.41      private final RequestProcessor requestProcessor = new RequestProcessor(DockerContainersChildFactory.class);
   24.42  
   24.43 @@ -90,6 +93,8 @@
   24.44  
   24.45      private final RequestProcessor.Task refreshTask;
   24.46  
   24.47 +    private final DockerEvent.Listener listener;
   24.48 +
   24.49      public DockerContainersChildFactory(DockerInstance instance) {
   24.50          this.instance = instance;
   24.51          this.refreshTask = requestProcessor.create(new Runnable() {
   24.52 @@ -99,35 +104,36 @@
   24.53                  refresh();
   24.54              }
   24.55          });
   24.56 -        instance.addContainerListener(new DockerEvent.Listener() {
   24.57 +        this.listener = new DockerEvent.Listener() {
   24.58              @Override
   24.59              public void onEvent(DockerEvent event) {
   24.60                  if (CHANGE_EVENTS.contains(event.getStatus())) {
   24.61                      refreshTask.schedule(200);
   24.62                  }
   24.63              }
   24.64 -        });
   24.65 +        };
   24.66 +        instance.addContainerListener(listener);
   24.67      }
   24.68  
   24.69      @Override
   24.70 -    protected Node createNodeForKey(EnhancedDockerContainer key) {
   24.71 +    protected Node createNodeForKey(StatefulDockerContainer key) {
   24.72          return new DockerContainerNode(key);
   24.73      }
   24.74  
   24.75      @Override
   24.76 -    protected boolean createKeys(List<EnhancedDockerContainer> toPopulate) {
   24.77 +    protected boolean createKeys(List<StatefulDockerContainer> toPopulate) {
   24.78          DockerAction facade = new DockerAction(instance);
   24.79          List<DockerContainer> containers = new ArrayList<>(facade.getContainers());
   24.80          Collections.sort(containers, COMPARATOR);
   24.81          synchronized (cache) {
   24.82              for (DockerContainer c : containers) {
   24.83 -                EnhancedDockerContainer cached = null;
   24.84 -                WeakReference<EnhancedDockerContainer> ref = cache.get(c);
   24.85 +                StatefulDockerContainer cached = null;
   24.86 +                WeakReference<StatefulDockerContainer> ref = cache.get(c);
   24.87                  if (ref != null) {
   24.88                      cached = ref.get();
   24.89                  }
   24.90                  if (cached == null) {
   24.91 -                    cached = new EnhancedDockerContainer(c);
   24.92 +                    cached = new StatefulDockerContainer(c);
   24.93                      cache.put(c, new WeakReference<>(cached));
   24.94                  } else {
   24.95                      cached.refresh();
   24.96 @@ -143,4 +149,17 @@
   24.97          refresh(false);
   24.98      }
   24.99  
  24.100 +    @Override
  24.101 +    public void close() {
  24.102 +        instance.removeContainerListener(listener);
  24.103 +        synchronized (cache) {
  24.104 +            for (WeakReference<StatefulDockerContainer> r : cache.values()) {
  24.105 +                StatefulDockerContainer c  = r.get();
  24.106 +                if (c != null) {
  24.107 +                    c.close();
  24.108 +                }
  24.109 +            }
  24.110 +        }
  24.111 +    }
  24.112 +
  24.113  }
    25.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/node/DockerImagesChildFactory.java	Wed Mar 23 21:02:01 2016 +0000
    25.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/node/DockerImagesChildFactory.java	Thu Mar 24 15:34:03 2016 +0100
    25.3 @@ -41,6 +41,7 @@
    25.4   */
    25.5  package org.netbeans.modules.docker.ui.node;
    25.6  
    25.7 +import java.io.Closeable;
    25.8  import java.util.ArrayList;
    25.9  import java.util.Collections;
   25.10  import java.util.Comparator;
   25.11 @@ -52,7 +53,6 @@
   25.12  import org.netbeans.modules.docker.api.DockerTag;
   25.13  import org.netbeans.modules.docker.api.DockerEvent;
   25.14  import org.netbeans.modules.docker.api.DockerAction;
   25.15 -import org.openide.nodes.ChildFactory;
   25.16  import org.openide.nodes.Node;
   25.17  import org.openide.util.RequestProcessor;
   25.18  
   25.19 @@ -60,7 +60,7 @@
   25.20   *
   25.21   * @author Petr Hejl
   25.22   */
   25.23 -public class DockerImagesChildFactory extends ChildFactory<DockerTag> implements Refreshable {
   25.24 +public class DockerImagesChildFactory extends NodeClosingFactory<DockerTag> implements Refreshable, Closeable {
   25.25  
   25.26      private static final Logger LOGGER = Logger.getLogger(DockerImagesChildFactory.class.getName());
   25.27  
   25.28 @@ -78,7 +78,7 @@
   25.29  
   25.30      private final RequestProcessor.Task refreshTask;
   25.31  
   25.32 -    private DockerEvent lastEvent;
   25.33 +    private final DockerEvent.Listener listener;
   25.34  
   25.35      public DockerImagesChildFactory(DockerInstance instance) {
   25.36          this.instance = instance;
   25.37 @@ -89,14 +89,15 @@
   25.38                  refresh();
   25.39              }
   25.40          });
   25.41 -        instance.addImageListener(new DockerEvent.Listener() {
   25.42 +        this.listener = new DockerEvent.Listener() {
   25.43              @Override
   25.44              public void onEvent(DockerEvent event) {
   25.45                  if (DockerEvent.Status.PUSH != event.getStatus()) {
   25.46                      refreshTask.schedule(200);
   25.47                  }
   25.48              }
   25.49 -        });
   25.50 +        };
   25.51 +        instance.addImageListener(listener);
   25.52      }
   25.53  
   25.54      @Override
   25.55 @@ -116,8 +117,14 @@
   25.56          return true;
   25.57      }
   25.58  
   25.59 +    @Override
   25.60      public final void refresh() {
   25.61          refresh(false);
   25.62      }
   25.63  
   25.64 +    @Override
   25.65 +    public void close() {
   25.66 +        instance.removeImageListener(listener);
   25.67 +    }
   25.68 +
   25.69  }
    26.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/node/DockerInstanceChildFactory.java	Wed Mar 23 21:02:01 2016 +0000
    26.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/node/DockerInstanceChildFactory.java	Thu Mar 24 15:34:03 2016 +0100
    26.3 @@ -41,22 +41,32 @@
    26.4   */
    26.5  package org.netbeans.modules.docker.ui.node;
    26.6  
    26.7 +import java.io.Closeable;
    26.8 +import java.io.IOException;
    26.9 +import java.util.Collections;
   26.10 +import java.util.HashSet;
   26.11  import java.util.List;
   26.12 +import java.util.Set;
   26.13 +import java.util.logging.Level;
   26.14 +import java.util.logging.Logger;
   26.15  import javax.swing.event.ChangeEvent;
   26.16  import javax.swing.event.ChangeListener;
   26.17  import org.netbeans.modules.docker.api.DockerInstance;
   26.18 -import org.openide.nodes.ChildFactory;
   26.19  import org.openide.nodes.Node;
   26.20  
   26.21  /**
   26.22   *
   26.23   * @author Petr Hejl
   26.24   */
   26.25 -public class DockerInstanceChildFactory extends ChildFactory<Boolean> {
   26.26 +public class DockerInstanceChildFactory extends NodeClosingFactory<Boolean> implements Closeable {
   26.27  
   26.28 -    private final EnhancedDockerInstance instance;
   26.29 +    private static final Logger LOGGER = Logger.getLogger(DockerInstanceChildFactory.class.getName());
   26.30  
   26.31 -    public DockerInstanceChildFactory(EnhancedDockerInstance instance) {
   26.32 +    private final StatefulDockerInstance instance;
   26.33 +
   26.34 +    private final Set<Node> current = new HashSet<>();
   26.35 +
   26.36 +    public DockerInstanceChildFactory(StatefulDockerInstance instance) {
   26.37          this.instance = instance;
   26.38  
   26.39          instance.addChangeListener(new ChangeListener() {
   26.40 @@ -70,15 +80,21 @@
   26.41  
   26.42      @Override
   26.43      protected Node[] createNodesForKey(Boolean key) {
   26.44 +        Node[] ret;
   26.45          if (key) {
   26.46              DockerInstance dockerInstance = instance.getInstance();
   26.47              DockerImagesChildFactory factoryRepo = new DockerImagesChildFactory(dockerInstance);
   26.48              DockerContainersChildFactory factoryCont = new DockerContainersChildFactory(dockerInstance);
   26.49 -            return new Node[]{new DockerImagesNode(dockerInstance, factoryRepo),
   26.50 +            ret = new Node[]{new DockerImagesNode(dockerInstance, factoryRepo),
   26.51                  new DockerContainersNode(dockerInstance, factoryCont)};
   26.52          } else {
   26.53 -            return new Node[] {};
   26.54 +            ret = new Node[] {};
   26.55          }
   26.56 +        synchronized (current) {
   26.57 +            current.clear();
   26.58 +            Collections.addAll(current, ret);
   26.59 +        }
   26.60 +        return ret;
   26.61      }
   26.62  
   26.63      @Override
   26.64 @@ -86,4 +102,21 @@
   26.65          toPopulate.add(instance.isAvailable());
   26.66          return true;
   26.67      }
   26.68 +
   26.69 +    @Override
   26.70 +    public void close() {
   26.71 +        Set<Node> nodes;
   26.72 +        synchronized (current) {
   26.73 +            nodes = new HashSet<>(current);
   26.74 +        }
   26.75 +        for (Node n : nodes) {
   26.76 +            for (Closeable c : n.getLookup().lookupAll(Closeable.class)) {
   26.77 +                try {
   26.78 +                    c.close();
   26.79 +                } catch (IOException ex) {
   26.80 +                    LOGGER.log(Level.INFO, null, ex);
   26.81 +                }
   26.82 +            }
   26.83 +        }
   26.84 +    }
   26.85  }
    27.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/node/DockerInstanceNode.java	Wed Mar 23 21:02:01 2016 +0000
    27.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/node/DockerInstanceNode.java	Thu Mar 24 15:34:03 2016 +0100
    27.3 @@ -61,7 +61,7 @@
    27.4  
    27.5      private static final String DOCKER_INSTANCE_ICON = "org/netbeans/modules/docker/ui/resources/docker_instance.png"; // NOI18N
    27.6  
    27.7 -    private final EnhancedDockerInstance instance;
    27.8 +    private final StatefulDockerInstance instance;
    27.9  
   27.10      private final ChangeListener listener = new ChangeListener() {
   27.11          @Override
   27.12 @@ -70,9 +70,9 @@
   27.13          }
   27.14      };
   27.15  
   27.16 -    public DockerInstanceNode(EnhancedDockerInstance instance) {
   27.17 -        super(Children.create(new DockerInstanceChildFactory(instance), true),
   27.18 -                Lookups.fixed(instance.getInstance(), instance));
   27.19 +    public DockerInstanceNode(StatefulDockerInstance instance, DockerInstanceChildFactory factory) {
   27.20 +        super(Children.create(factory, true),
   27.21 +                Lookups.fixed(instance.getInstance(), instance, factory));
   27.22          this.instance = instance;
   27.23          setIconBaseWithExtension(DOCKER_INSTANCE_ICON);
   27.24          setShortDescription(instance.getInstance().getUrl());
    28.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/node/DockerNode.java	Wed Mar 23 21:02:01 2016 +0000
    28.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/node/DockerNode.java	Thu Mar 24 15:34:03 2016 +0100
    28.3 @@ -44,19 +44,16 @@
    28.4  
    28.5  package org.netbeans.modules.docker.ui.node;
    28.6  
    28.7 -import java.io.Serializable;
    28.8  import java.util.ArrayList;
    28.9  import java.util.Collections;
   28.10 -import java.util.Comparator;
   28.11  import java.util.List;
   28.12 -import java.util.logging.Level;
   28.13  import java.util.logging.Logger;
   28.14  import javax.swing.Action;
   28.15  import javax.swing.event.ChangeEvent;
   28.16  import javax.swing.event.ChangeListener;
   28.17  import org.netbeans.api.core.ide.ServicesTabNodeRegistration;
   28.18  import org.netbeans.modules.docker.api.DockerInstance;
   28.19 -import org.netbeans.modules.docker.api.DockerIntegration;
   28.20 +import org.netbeans.modules.docker.api.DockerSupport;
   28.21  import org.netbeans.modules.docker.ui.UiUtils;
   28.22  import org.openide.nodes.AbstractNode;
   28.23  import org.openide.nodes.Children;
   28.24 @@ -68,14 +65,11 @@
   28.25  
   28.26  public final class DockerNode extends AbstractNode {
   28.27  
   28.28 -    private static final RequestProcessor REFRESH_PROCESSOR =
   28.29 -            new RequestProcessor("Docker node update/refresh", 5);
   28.30 -
   28.31      private static final String DOCKER_ICON = "org/netbeans/modules/docker/ui/resources/docker_root.png"; // NOI18N
   28.32  
   28.33      private static DockerNode node;
   28.34  
   28.35 -    private DockerNode(ChildFactory factory, String displayName, String shortDesc, String iconBase) {
   28.36 +    private DockerNode(DockerChildFactory factory, String displayName, String shortDesc, String iconBase) {
   28.37          super(Children.create(factory, true));
   28.38  
   28.39          setName(""); // NOI18N
   28.40 @@ -93,7 +87,7 @@
   28.41      )
   28.42      public static synchronized DockerNode getInstance() {
   28.43          if (node == null) {
   28.44 -            ChildFactory factory = new ChildFactory(DockerIntegration.getDefault());
   28.45 +            DockerChildFactory factory = new DockerChildFactory(DockerSupport.getDefault());
   28.46              factory.init();
   28.47  
   28.48              node = new DockerNode(factory,
   28.49 @@ -113,60 +107,4 @@
   28.50          return ret.toArray(new Action[ret.size()]);
   28.51      }
   28.52  
   28.53 -    private static class ChildFactory extends org.openide.nodes.ChildFactory<EnhancedDockerInstance>
   28.54 -            implements ChangeListener {
   28.55 -
   28.56 -        private final DockerIntegration registry;
   28.57 -
   28.58 -        public ChildFactory(DockerIntegration registry) {
   28.59 -            super();
   28.60 -            this.registry = registry;
   28.61 -        }
   28.62 -
   28.63 -        public void init() {
   28.64 -            REFRESH_PROCESSOR.post(new Runnable() {
   28.65 -
   28.66 -                public void run() {
   28.67 -                    synchronized (ChildFactory.this) {
   28.68 -                        registry.addChangeListener(
   28.69 -                            WeakListeners.create(ChangeListener.class, ChildFactory.this, registry));
   28.70 -                        updateState(new ChangeEvent(registry));
   28.71 -                    }
   28.72 -                }
   28.73 -            });
   28.74 -        }
   28.75 -
   28.76 -        public void stateChanged(final ChangeEvent e) {
   28.77 -            REFRESH_PROCESSOR.post(new Runnable() {
   28.78 -
   28.79 -                public void run() {
   28.80 -                    updateState(e);
   28.81 -                }
   28.82 -            });
   28.83 -        }
   28.84 -
   28.85 -        private synchronized void updateState(final ChangeEvent e) {
   28.86 -            refresh();
   28.87 -        }
   28.88 -
   28.89 -        protected final void refresh() {
   28.90 -            refresh(false);
   28.91 -        }
   28.92 -
   28.93 -        @Override
   28.94 -        protected Node createNodeForKey(EnhancedDockerInstance key) {
   28.95 -            return new DockerInstanceNode(key);
   28.96 -        }
   28.97 -
   28.98 -        @Override
   28.99 -        protected boolean createKeys(List<EnhancedDockerInstance> toPopulate) {
  28.100 -            List<DockerInstance> fresh = new ArrayList<>(registry.getInstances());
  28.101 -            Collections.sort(fresh, UiUtils.getInstanceComparator());
  28.102 -            for (DockerInstance i : fresh) {
  28.103 -                toPopulate.add(new EnhancedDockerInstance(i));
  28.104 -            }
  28.105 -            return true;
  28.106 -        }
  28.107 -
  28.108 -    } // end of ChildFactory
  28.109  }
    29.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/node/EnhancedDockerContainer.java	Wed Mar 23 21:02:01 2016 +0000
    29.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    29.3 @@ -1,182 +0,0 @@
    29.4 -/*
    29.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    29.6 - *
    29.7 - * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
    29.8 - *
    29.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   29.10 - * Other names may be trademarks of their respective owners.
   29.11 - *
   29.12 - * The contents of this file are subject to the terms of either the GNU
   29.13 - * General Public License Version 2 only ("GPL") or the Common
   29.14 - * Development and Distribution License("CDDL") (collectively, the
   29.15 - * "License"). You may not use this file except in compliance with the
   29.16 - * License. You can obtain a copy of the License at
   29.17 - * http://www.netbeans.org/cddl-gplv2.html
   29.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   29.19 - * specific language governing permissions and limitations under the
   29.20 - * License.  When distributing the software, include this License Header
   29.21 - * Notice in each file and include the License file at
   29.22 - * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   29.23 - * particular file as subject to the "Classpath" exception as provided
   29.24 - * by Oracle in the GPL Version 2 section of the License file that
   29.25 - * accompanied this code. If applicable, add the following below the
   29.26 - * License Header, with the fields enclosed by brackets [] replaced by
   29.27 - * your own identifying information:
   29.28 - * "Portions Copyrighted [year] [name of copyright owner]"
   29.29 - *
   29.30 - * If you wish your version of this file to be governed by only the CDDL
   29.31 - * or only the GPL Version 2, indicate your decision by adding
   29.32 - * "[Contributor] elects to include this software in this distribution
   29.33 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
   29.34 - * single choice of license, a recipient has the option to distribute
   29.35 - * your version of this file under either the CDDL, the GPL Version 2 or
   29.36 - * to extend the choice of license to its licensees as provided above.
   29.37 - * However, if you add GPL Version 2 code and therefore, elected the GPL
   29.38 - * Version 2 license, then the option applies only if the new code is
   29.39 - * made subject to such option by the copyright holder.
   29.40 - *
   29.41 - * Contributor(s):
   29.42 - *
   29.43 - * Portions Copyrighted 2015 Sun Microsystems, Inc.
   29.44 - */
   29.45 -package org.netbeans.modules.docker.ui.node;
   29.46 -
   29.47 -import java.util.Objects;
   29.48 -import java.util.logging.Level;
   29.49 -import java.util.logging.Logger;
   29.50 -import javax.swing.event.ChangeListener;
   29.51 -import org.netbeans.modules.docker.api.DockerAction;
   29.52 -import org.netbeans.modules.docker.api.DockerContainer;
   29.53 -import org.netbeans.modules.docker.api.DockerContainerDetail;
   29.54 -import org.netbeans.modules.docker.api.DockerEvent;
   29.55 -import org.netbeans.modules.docker.api.DockerException;
   29.56 -import org.openide.util.ChangeSupport;
   29.57 -import org.openide.util.RequestProcessor;
   29.58 -import org.openide.util.WeakListeners;
   29.59 -
   29.60 -/**
   29.61 - *
   29.62 - * @author Petr Hejl
   29.63 - */
   29.64 -public class EnhancedDockerContainer implements Refreshable {
   29.65 -
   29.66 -    private static final Logger LOGGER = Logger.getLogger(EnhancedDockerContainer.class.getName());
   29.67 -
   29.68 -    private static final RequestProcessor RP = new RequestProcessor(EnhancedDockerContainer.class);
   29.69 -
   29.70 -    private final ChangeSupport changeSupport = new ChangeSupport(this);
   29.71 -
   29.72 -    private final DockerEvent.Listener listener = new DockerEvent.Listener() {
   29.73 -        @Override
   29.74 -        public void onEvent(DockerEvent event) {
   29.75 -            if (event.getId().equals(EnhancedDockerContainer.this.container.getId())
   29.76 -                    && event.getStatus() != DockerEvent.Status.DESTROY && event.getStatus() != DockerEvent.Status.UNTAG) {
   29.77 -                DockerContainer.Status fresh = getStatus(event);
   29.78 -                if (fresh != null) {
   29.79 -                    update(fresh);
   29.80 -                } else {
   29.81 -                    refresh();
   29.82 -                }
   29.83 -            }
   29.84 -        }
   29.85 -    };
   29.86 -
   29.87 -    private final DockerContainer container;
   29.88 -
   29.89 -    private DockerContainerDetail detail;
   29.90 -
   29.91 -    public EnhancedDockerContainer(DockerContainer container) {
   29.92 -        this.container = container;
   29.93 -        this.detail = new DockerContainerDetail(container.getName(), container.getStatus(), false, false);
   29.94 -        container.getInstance().addContainerListener(
   29.95 -                WeakListeners.create(DockerEvent.Listener.class, listener, container.getInstance()));
   29.96 -    }
   29.97 -
   29.98 -    public void addChangeListener(ChangeListener listener) {
   29.99 -        changeSupport.addChangeListener(listener);
  29.100 -    }
  29.101 -
  29.102 -    public void removeChangeListener(ChangeListener listener) {
  29.103 -        changeSupport.removeChangeListener(listener);
  29.104 -    }
  29.105 -
  29.106 -    public DockerContainer getContainer() {
  29.107 -        return container;
  29.108 -    }
  29.109 -
  29.110 -    public DockerContainerDetail getDetail() {
  29.111 -        synchronized (this) {
  29.112 -            return detail;
  29.113 -        }
  29.114 -    }
  29.115 -
  29.116 -    @Override
  29.117 -    public void refresh() {
  29.118 -        RP.post(new Runnable() {
  29.119 -            @Override
  29.120 -            public void run() {
  29.121 -                DockerAction action = new DockerAction(container.getInstance());
  29.122 -                try {
  29.123 -                    update(action.getDetail(container));
  29.124 -                } catch (DockerException ex) {
  29.125 -                    LOGGER.log(Level.INFO, null, ex);
  29.126 -                }
  29.127 -            }
  29.128 -        });
  29.129 -    }
  29.130 -
  29.131 -    @Override
  29.132 -    public int hashCode() {
  29.133 -        int hash = 3;
  29.134 -        hash = 71 * hash + Objects.hashCode(this.container);
  29.135 -        return hash;
  29.136 -    }
  29.137 -
  29.138 -    @Override
  29.139 -    public boolean equals(Object obj) {
  29.140 -        if (this == obj) {
  29.141 -            return true;
  29.142 -        }
  29.143 -        if (obj == null) {
  29.144 -            return false;
  29.145 -        }
  29.146 -        if (getClass() != obj.getClass()) {
  29.147 -            return false;
  29.148 -        }
  29.149 -        final EnhancedDockerContainer other = (EnhancedDockerContainer) obj;
  29.150 -        if (!Objects.equals(this.container, other.container)) {
  29.151 -            return false;
  29.152 -        }
  29.153 -        return true;
  29.154 -    }
  29.155 -
  29.156 -    private void update(DockerContainer.Status status) {
  29.157 -        synchronized (this) {
  29.158 -            detail = new DockerContainerDetail(detail.getName(), status, detail.isStdin(), detail.isTty());
  29.159 -        }
  29.160 -        changeSupport.fireChange();
  29.161 -    }
  29.162 -
  29.163 -    private void update(DockerContainerDetail value) {
  29.164 -        synchronized (this) {
  29.165 -            detail = value;
  29.166 -        }
  29.167 -        changeSupport.fireChange();
  29.168 -    }
  29.169 -
  29.170 -    private static DockerContainer.Status getStatus(DockerEvent event) {
  29.171 -        DockerEvent.Status status = event.getStatus();
  29.172 -        switch (status) {
  29.173 -            case DIE:
  29.174 -                return DockerContainer.Status.STOPPED;
  29.175 -            case START:
  29.176 -                return DockerContainer.Status.RUNNING;
  29.177 -            case PAUSE:
  29.178 -                return DockerContainer.Status.PAUSED;
  29.179 -            case UNPAUSE:
  29.180 -                return DockerContainer.Status.RUNNING;
  29.181 -            default:
  29.182 -                return null;
  29.183 -        }
  29.184 -    }
  29.185 -}
    30.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/node/EnhancedDockerInstance.java	Wed Mar 23 21:02:01 2016 +0000
    30.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.3 @@ -1,146 +0,0 @@
    30.4 -/*
    30.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    30.6 - *
    30.7 - * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
    30.8 - *
    30.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   30.10 - * Other names may be trademarks of their respective owners.
   30.11 - *
   30.12 - * The contents of this file are subject to the terms of either the GNU
   30.13 - * General Public License Version 2 only ("GPL") or the Common
   30.14 - * Development and Distribution License("CDDL") (collectively, the
   30.15 - * "License"). You may not use this file except in compliance with the
   30.16 - * License. You can obtain a copy of the License at
   30.17 - * http://www.netbeans.org/cddl-gplv2.html
   30.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   30.19 - * specific language governing permissions and limitations under the
   30.20 - * License.  When distributing the software, include this License Header
   30.21 - * Notice in each file and include the License file at
   30.22 - * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   30.23 - * particular file as subject to the "Classpath" exception as provided
   30.24 - * by Oracle in the GPL Version 2 section of the License file that
   30.25 - * accompanied this code. If applicable, add the following below the
   30.26 - * License Header, with the fields enclosed by brackets [] replaced by
   30.27 - * your own identifying information:
   30.28 - * "Portions Copyrighted [year] [name of copyright owner]"
   30.29 - *
   30.30 - * If you wish your version of this file to be governed by only the CDDL
   30.31 - * or only the GPL Version 2, indicate your decision by adding
   30.32 - * "[Contributor] elects to include this software in this distribution
   30.33 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
   30.34 - * single choice of license, a recipient has the option to distribute
   30.35 - * your version of this file under either the CDDL, the GPL Version 2 or
   30.36 - * to extend the choice of license to its licensees as provided above.
   30.37 - * However, if you add GPL Version 2 code and therefore, elected the GPL
   30.38 - * Version 2 license, then the option applies only if the new code is
   30.39 - * made subject to such option by the copyright holder.
   30.40 - *
   30.41 - * Contributor(s):
   30.42 - *
   30.43 - * Portions Copyrighted 2015 Sun Microsystems, Inc.
   30.44 - */
   30.45 -package org.netbeans.modules.docker.ui.node;
   30.46 -
   30.47 -import java.util.Objects;
   30.48 -import java.util.concurrent.atomic.AtomicBoolean;
   30.49 -import javax.swing.event.ChangeListener;
   30.50 -import org.netbeans.modules.docker.api.DockerInstance;
   30.51 -import org.openide.util.ChangeSupport;
   30.52 -import org.openide.util.RequestProcessor;
   30.53 -import org.openide.util.WeakListeners;
   30.54 -
   30.55 -/**
   30.56 - *
   30.57 - * @author Petr Hejl
   30.58 - */
   30.59 -public class EnhancedDockerInstance implements Refreshable {
   30.60 -
   30.61 -    private static final RequestProcessor RP = new RequestProcessor(EnhancedDockerInstance.class);
   30.62 -
   30.63 -    private final ChangeSupport changeSupport = new ChangeSupport(this);
   30.64 -
   30.65 -    // FIXME default value
   30.66 -    private final AtomicBoolean available = new AtomicBoolean(true);
   30.67 -
   30.68 -    private final DockerInstance.ConnectionListener listener = new DockerInstance.ConnectionListener() {
   30.69 -        @Override
   30.70 -        public void onConnect() {
   30.71 -            update(true);
   30.72 -        }
   30.73 -
   30.74 -        @Override
   30.75 -        public void onDisconnect() {
   30.76 -            update(false);
   30.77 -        }
   30.78 -    };
   30.79 -
   30.80 -    private final DockerInstance instance;
   30.81 -
   30.82 -    public EnhancedDockerInstance(DockerInstance instance) {
   30.83 -        this.instance = instance;
   30.84 -        instance.addConnectionListener(listener);
   30.85 -    }
   30.86 -
   30.87 -    public void addChangeListener(ChangeListener listener) {
   30.88 -        changeSupport.addChangeListener(listener);
   30.89 -    }
   30.90 -
   30.91 -    public void removeChangeListener(ChangeListener listener) {
   30.92 -        changeSupport.removeChangeListener(listener);
   30.93 -    }
   30.94 -
   30.95 -    public DockerInstance getInstance() {
   30.96 -        return instance;
   30.97 -    }
   30.98 -
   30.99 -    public boolean isAvailable() {
  30.100 -        return available.get();
  30.101 -    }
  30.102 -
  30.103 -    @Override
  30.104 -    public void refresh() {
  30.105 -        RP.post(new Runnable() {
  30.106 -            @Override
  30.107 -            public void run() {
  30.108 -                update(instance.isAvailable());
  30.109 -            }
  30.110 -        });
  30.111 -    }
  30.112 -    
  30.113 -    public void remove() {
  30.114 -        instance.removeConnectionListener(listener);
  30.115 -        instance.remove();
  30.116 -    }
  30.117 -
  30.118 -    @Override
  30.119 -    public int hashCode() {
  30.120 -        int hash = 7;
  30.121 -        hash = 61 * hash + Objects.hashCode(this.instance);
  30.122 -        return hash;
  30.123 -    }
  30.124 -
  30.125 -    @Override
  30.126 -    public boolean equals(Object obj) {
  30.127 -        if (this == obj) {
  30.128 -            return true;
  30.129 -        }
  30.130 -        if (obj == null) {
  30.131 -            return false;
  30.132 -        }
  30.133 -        if (getClass() != obj.getClass()) {
  30.134 -            return false;
  30.135 -        }
  30.136 -        final EnhancedDockerInstance other = (EnhancedDockerInstance) obj;
  30.137 -        if (!Objects.equals(this.instance, other.instance)) {
  30.138 -            return false;
  30.139 -        }
  30.140 -        return true;
  30.141 -    }
  30.142 -
  30.143 -    private void update(boolean newValue) {
  30.144 -        boolean oldValue = available.getAndSet(newValue);
  30.145 -        if (oldValue != newValue) {
  30.146 -            changeSupport.fireChange();
  30.147 -        }
  30.148 -    }
  30.149 -}
    31.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    31.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/node/NodeClosingFactory.java	Thu Mar 24 15:34:03 2016 +0100
    31.3 @@ -0,0 +1,72 @@
    31.4 +/*
    31.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    31.6 + *
    31.7 + * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
    31.8 + *
    31.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   31.10 + * Other names may be trademarks of their respective owners.
   31.11 + *
   31.12 + * The contents of this file are subject to the terms of either the GNU
   31.13 + * General Public License Version 2 only ("GPL") or the Common
   31.14 + * Development and Distribution License("CDDL") (collectively, the
   31.15 + * "License"). You may not use this file except in compliance with the
   31.16 + * License. You can obtain a copy of the License at
   31.17 + * http://www.netbeans.org/cddl-gplv2.html
   31.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   31.19 + * specific language governing permissions and limitations under the
   31.20 + * License.  When distributing the software, include this License Header
   31.21 + * Notice in each file and include the License file at
   31.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   31.23 + * particular file as subject to the "Classpath" exception as provided
   31.24 + * by Oracle in the GPL Version 2 section of the License file that
   31.25 + * accompanied this code. If applicable, add the following below the
   31.26 + * License Header, with the fields enclosed by brackets [] replaced by
   31.27 + * your own identifying information:
   31.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   31.29 + *
   31.30 + * If you wish your version of this file to be governed by only the CDDL
   31.31 + * or only the GPL Version 2, indicate your decision by adding
   31.32 + * "[Contributor] elects to include this software in this distribution
   31.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   31.34 + * single choice of license, a recipient has the option to distribute
   31.35 + * your version of this file under either the CDDL, the GPL Version 2 or
   31.36 + * to extend the choice of license to its licensees as provided above.
   31.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   31.38 + * Version 2 license, then the option applies only if the new code is
   31.39 + * made subject to such option by the copyright holder.
   31.40 + *
   31.41 + * Contributor(s):
   31.42 + *
   31.43 + * Portions Copyrighted 2016 Sun Microsystems, Inc.
   31.44 + */
   31.45 +package org.netbeans.modules.docker.ui.node;
   31.46 +
   31.47 +import java.io.Closeable;
   31.48 +import java.io.IOException;
   31.49 +import java.util.logging.Level;
   31.50 +import java.util.logging.Logger;
   31.51 +import org.openide.nodes.DestroyableNodesFactory;
   31.52 +import org.openide.nodes.Node;
   31.53 +
   31.54 +/**
   31.55 + *
   31.56 + * @author Petr Hejl
   31.57 + */
   31.58 +public abstract class NodeClosingFactory<T> extends DestroyableNodesFactory<T> {
   31.59 +
   31.60 +    private static final Logger LOGGER = Logger.getLogger(NodeClosingFactory.class.getName());
   31.61 +
   31.62 +    @Override
   31.63 +    protected void destroyNodes(Node[] arr) {
   31.64 +        for (Node n : arr) {
   31.65 +            for (Closeable c : n.getLookup().lookupAll(Closeable.class)) {
   31.66 +                try {
   31.67 +                    LOGGER.log(Level.FINE, "Closing {0}", c.getClass().getName());
   31.68 +                    c.close();
   31.69 +                } catch (IOException ex) {
   31.70 +                    LOGGER.log(Level.FINE, null, ex);
   31.71 +                }
   31.72 +            }
   31.73 +        }
   31.74 +    }
   31.75 +}
    32.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/node/RemoveInstanceAction.java	Wed Mar 23 21:02:01 2016 +0000
    32.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/node/RemoveInstanceAction.java	Thu Mar 24 15:34:03 2016 +0100
    32.3 @@ -56,7 +56,7 @@
    32.4      @Override
    32.5      protected void performAction(Node[] activatedNodes) {
    32.6          for (Node node : activatedNodes) {
    32.7 -            EnhancedDockerInstance instance = node.getLookup().lookup(EnhancedDockerInstance.class);
    32.8 +            StatefulDockerInstance instance = node.getLookup().lookup(StatefulDockerInstance.class);
    32.9              if (instance != null) {
   32.10                  instance.remove();
   32.11              }
    33.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    33.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/node/StatefulDockerContainer.java	Thu Mar 24 15:34:03 2016 +0100
    33.3 @@ -0,0 +1,190 @@
    33.4 +/*
    33.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    33.6 + *
    33.7 + * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
    33.8 + *
    33.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   33.10 + * Other names may be trademarks of their respective owners.
   33.11 + *
   33.12 + * The contents of this file are subject to the terms of either the GNU
   33.13 + * General Public License Version 2 only ("GPL") or the Common
   33.14 + * Development and Distribution License("CDDL") (collectively, the
   33.15 + * "License"). You may not use this file except in compliance with the
   33.16 + * License. You can obtain a copy of the License at
   33.17 + * http://www.netbeans.org/cddl-gplv2.html
   33.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   33.19 + * specific language governing permissions and limitations under the
   33.20 + * License.  When distributing the software, include this License Header
   33.21 + * Notice in each file and include the License file at
   33.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   33.23 + * particular file as subject to the "Classpath" exception as provided
   33.24 + * by Oracle in the GPL Version 2 section of the License file that
   33.25 + * accompanied this code. If applicable, add the following below the
   33.26 + * License Header, with the fields enclosed by brackets [] replaced by
   33.27 + * your own identifying information:
   33.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   33.29 + *
   33.30 + * If you wish your version of this file to be governed by only the CDDL
   33.31 + * or only the GPL Version 2, indicate your decision by adding
   33.32 + * "[Contributor] elects to include this software in this distribution
   33.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   33.34 + * single choice of license, a recipient has the option to distribute
   33.35 + * your version of this file under either the CDDL, the GPL Version 2 or
   33.36 + * to extend the choice of license to its licensees as provided above.
   33.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   33.38 + * Version 2 license, then the option applies only if the new code is
   33.39 + * made subject to such option by the copyright holder.
   33.40 + *
   33.41 + * Contributor(s):
   33.42 + *
   33.43 + * Portions Copyrighted 2015 Sun Microsystems, Inc.
   33.44 + */
   33.45 +package org.netbeans.modules.docker.ui.node;
   33.46 +
   33.47 +import java.io.Closeable;
   33.48 +import java.util.Objects;
   33.49 +import java.util.logging.Level;
   33.50 +import java.util.logging.Logger;
   33.51 +import javax.swing.event.ChangeListener;
   33.52 +import org.netbeans.modules.docker.api.DockerAction;
   33.53 +import org.netbeans.modules.docker.api.DockerContainer;
   33.54 +import org.netbeans.modules.docker.api.DockerContainerDetail;
   33.55 +import org.netbeans.modules.docker.api.DockerEvent;
   33.56 +import org.netbeans.modules.docker.api.DockerException;
   33.57 +import org.openide.util.ChangeSupport;
   33.58 +import org.openide.util.RequestProcessor;
   33.59 +import org.openide.util.WeakListeners;
   33.60 +
   33.61 +/**
   33.62 + *
   33.63 + * @author Petr Hejl
   33.64 + */
   33.65 +public class StatefulDockerContainer implements Refreshable, Closeable {
   33.66 +
   33.67 +    private static final Logger LOGGER = Logger.getLogger(StatefulDockerContainer.class.getName());
   33.68 +
   33.69 +    private static final RequestProcessor RP = new RequestProcessor(StatefulDockerContainer.class);
   33.70 +
   33.71 +    private final ChangeSupport changeSupport = new ChangeSupport(this);
   33.72 +
   33.73 +    private final DockerEvent.Listener listener = new DockerEvent.Listener() {
   33.74 +        @Override
   33.75 +        public void onEvent(DockerEvent event) {
   33.76 +            if (event.getId().equals(StatefulDockerContainer.this.container.getId())
   33.77 +                    && event.getStatus() != DockerEvent.Status.DESTROY && event.getStatus() != DockerEvent.Status.UNTAG) {
   33.78 +                DockerContainer.Status fresh = getStatus(event);
   33.79 +                if (fresh != null) {
   33.80 +                    update(fresh);
   33.81 +                } else {
   33.82 +                    refresh();
   33.83 +                }
   33.84 +            }
   33.85 +        }
   33.86 +    };
   33.87 +
   33.88 +    private final DockerEvent.Listener weak;
   33.89 +
   33.90 +    private final DockerContainer container;
   33.91 +
   33.92 +    private DockerContainerDetail detail;
   33.93 +
   33.94 +    public StatefulDockerContainer(DockerContainer container) {
   33.95 +        this.container = container;
   33.96 +        this.detail = new DockerContainerDetail(container.getName(), container.getStatus(), false, false);
   33.97 +        this.weak = WeakListeners.create(DockerEvent.Listener.class, listener, container.getInstance());
   33.98 +        container.getInstance().addContainerListener(weak);
   33.99 +    }
  33.100 +
  33.101 +    public void addChangeListener(ChangeListener listener) {
  33.102 +        changeSupport.addChangeListener(listener);
  33.103 +    }
  33.104 +
  33.105 +    public void removeChangeListener(ChangeListener listener) {
  33.106 +        changeSupport.removeChangeListener(listener);
  33.107 +    }
  33.108 +
  33.109 +    public DockerContainer getContainer() {
  33.110 +        return container;
  33.111 +    }
  33.112 +
  33.113 +    public DockerContainerDetail getDetail() {
  33.114 +        synchronized (this) {
  33.115 +            return detail;
  33.116 +        }
  33.117 +    }
  33.118 +
  33.119 +    @Override
  33.120 +    public void refresh() {
  33.121 +        RP.post(new Runnable() {
  33.122 +            @Override
  33.123 +            public void run() {
  33.124 +                DockerAction action = new DockerAction(container.getInstance());
  33.125 +                try {
  33.126 +                    update(action.getDetail(container));
  33.127 +                } catch (DockerException ex) {
  33.128 +                    LOGGER.log(Level.INFO, null, ex);
  33.129 +                }
  33.130 +            }
  33.131 +        });
  33.132 +    }
  33.133 +
  33.134 +    @Override
  33.135 +    public void close() {
  33.136 +        container.getInstance().removeContainerListener(weak);
  33.137 +    }
  33.138 +
  33.139 +    @Override
  33.140 +    public int hashCode() {
  33.141 +        int hash = 3;
  33.142 +        hash = 71 * hash + Objects.hashCode(this.container);
  33.143 +        return hash;
  33.144 +    }
  33.145 +
  33.146 +    @Override
  33.147 +    public boolean equals(Object obj) {
  33.148 +        if (this == obj) {
  33.149 +            return true;
  33.150 +        }
  33.151 +        if (obj == null) {
  33.152 +            return false;
  33.153 +        }
  33.154 +        if (getClass() != obj.getClass()) {
  33.155 +            return false;
  33.156 +        }
  33.157 +        final StatefulDockerContainer other = (StatefulDockerContainer) obj;
  33.158 +        if (!Objects.equals(this.container, other.container)) {
  33.159 +            return false;
  33.160 +        }
  33.161 +        return true;
  33.162 +    }
  33.163 +
  33.164 +    private void update(DockerContainer.Status status) {
  33.165 +        synchronized (this) {
  33.166 +            detail = new DockerContainerDetail(detail.getName(), status, detail.isStdin(), detail.isTty());
  33.167 +        }
  33.168 +        changeSupport.fireChange();
  33.169 +    }
  33.170 +
  33.171 +    private void update(DockerContainerDetail value) {
  33.172 +        synchronized (this) {
  33.173 +            detail = value;
  33.174 +        }
  33.175 +        changeSupport.fireChange();
  33.176 +    }
  33.177 +
  33.178 +    private static DockerContainer.Status getStatus(DockerEvent event) {
  33.179 +        DockerEvent.Status status = event.getStatus();
  33.180 +        switch (status) {
  33.181 +            case DIE:
  33.182 +                return DockerContainer.Status.STOPPED;
  33.183 +            case START:
  33.184 +                return DockerContainer.Status.RUNNING;
  33.185 +            case PAUSE:
  33.186 +                return DockerContainer.Status.PAUSED;
  33.187 +            case UNPAUSE:
  33.188 +                return DockerContainer.Status.RUNNING;
  33.189 +            default:
  33.190 +                return null;
  33.191 +        }
  33.192 +    }
  33.193 +}
    34.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    34.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/node/StatefulDockerInstance.java	Thu Mar 24 15:34:03 2016 +0100
    34.3 @@ -0,0 +1,147 @@
    34.4 +/*
    34.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    34.6 + *
    34.7 + * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
    34.8 + *
    34.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   34.10 + * Other names may be trademarks of their respective owners.
   34.11 + *
   34.12 + * The contents of this file are subject to the terms of either the GNU
   34.13 + * General Public License Version 2 only ("GPL") or the Common
   34.14 + * Development and Distribution License("CDDL") (collectively, the
   34.15 + * "License"). You may not use this file except in compliance with the
   34.16 + * License. You can obtain a copy of the License at
   34.17 + * http://www.netbeans.org/cddl-gplv2.html
   34.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   34.19 + * specific language governing permissions and limitations under the
   34.20 + * License.  When distributing the software, include this License Header
   34.21 + * Notice in each file and include the License file at
   34.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   34.23 + * particular file as subject to the "Classpath" exception as provided
   34.24 + * by Oracle in the GPL Version 2 section of the License file that
   34.25 + * accompanied this code. If applicable, add the following below the
   34.26 + * License Header, with the fields enclosed by brackets [] replaced by
   34.27 + * your own identifying information:
   34.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   34.29 + *
   34.30 + * If you wish your version of this file to be governed by only the CDDL
   34.31 + * or only the GPL Version 2, indicate your decision by adding
   34.32 + * "[Contributor] elects to include this software in this distribution
   34.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   34.34 + * single choice of license, a recipient has the option to distribute
   34.35 + * your version of this file under either the CDDL, the GPL Version 2 or
   34.36 + * to extend the choice of license to its licensees as provided above.
   34.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   34.38 + * Version 2 license, then the option applies only if the new code is
   34.39 + * made subject to such option by the copyright holder.
   34.40 + *
   34.41 + * Contributor(s):
   34.42 + *
   34.43 + * Portions Copyrighted 2015 Sun Microsystems, Inc.
   34.44 + */
   34.45 +package org.netbeans.modules.docker.ui.node;
   34.46 +
   34.47 +import java.util.Objects;
   34.48 +import java.util.concurrent.atomic.AtomicBoolean;
   34.49 +import javax.swing.event.ChangeListener;
   34.50 +import org.netbeans.modules.docker.api.DockerAction;
   34.51 +import org.netbeans.modules.docker.api.DockerInstance;
   34.52 +import org.netbeans.modules.docker.api.DockerSupport;
   34.53 +import org.openide.util.ChangeSupport;
   34.54 +import org.openide.util.RequestProcessor;
   34.55 +
   34.56 +/**
   34.57 + *
   34.58 + * @author Petr Hejl
   34.59 + */
   34.60 +public class StatefulDockerInstance implements Refreshable {
   34.61 +
   34.62 +    private static final RequestProcessor RP = new RequestProcessor(StatefulDockerInstance.class);
   34.63 +
   34.64 +    private final ChangeSupport changeSupport = new ChangeSupport(this);
   34.65 +
   34.66 +    // FIXME default value
   34.67 +    private final AtomicBoolean available = new AtomicBoolean(true);
   34.68 +
   34.69 +    private final DockerInstance.ConnectionListener listener = new DockerInstance.ConnectionListener() {
   34.70 +        @Override
   34.71 +        public void onConnect() {
   34.72 +            update(true);
   34.73 +        }
   34.74 +
   34.75 +        @Override
   34.76 +        public void onDisconnect() {
   34.77 +            update(false);
   34.78 +        }
   34.79 +    };
   34.80 +
   34.81 +    private final DockerInstance instance;
   34.82 +
   34.83 +    public StatefulDockerInstance(DockerInstance instance) {
   34.84 +        this.instance = instance;
   34.85 +        instance.addConnectionListener(listener);
   34.86 +    }
   34.87 +
   34.88 +    public void addChangeListener(ChangeListener listener) {
   34.89 +        changeSupport.addChangeListener(listener);
   34.90 +    }
   34.91 +
   34.92 +    public void removeChangeListener(ChangeListener listener) {
   34.93 +        changeSupport.removeChangeListener(listener);
   34.94 +    }
   34.95 +
   34.96 +    public DockerInstance getInstance() {
   34.97 +        return instance;
   34.98 +    }
   34.99 +
  34.100 +    public boolean isAvailable() {
  34.101 +        return available.get();
  34.102 +    }
  34.103 +
  34.104 +    @Override
  34.105 +    public void refresh() {
  34.106 +        RP.post(new Runnable() {
  34.107 +            @Override
  34.108 +            public void run() {
  34.109 +                update(new DockerAction(instance).ping());
  34.110 +            }
  34.111 +        });
  34.112 +    }
  34.113 +
  34.114 +    public void remove() {
  34.115 +        instance.removeConnectionListener(listener);
  34.116 +        DockerSupport.getDefault().removeInstance(instance);
  34.117 +    }
  34.118 +
  34.119 +    @Override
  34.120 +    public int hashCode() {
  34.121 +        int hash = 7;
  34.122 +        hash = 61 * hash + Objects.hashCode(this.instance);
  34.123 +        return hash;
  34.124 +    }
  34.125 +
  34.126 +    @Override
  34.127 +    public boolean equals(Object obj) {
  34.128 +        if (this == obj) {
  34.129 +            return true;
  34.130 +        }
  34.131 +        if (obj == null) {
  34.132 +            return false;
  34.133 +        }
  34.134 +        if (getClass() != obj.getClass()) {
  34.135 +            return false;
  34.136 +        }
  34.137 +        final StatefulDockerInstance other = (StatefulDockerInstance) obj;
  34.138 +        if (!Objects.equals(this.instance, other.instance)) {
  34.139 +            return false;
  34.140 +        }
  34.141 +        return true;
  34.142 +    }
  34.143 +
  34.144 +    private void update(boolean newValue) {
  34.145 +        boolean oldValue = available.getAndSet(newValue);
  34.146 +        if (oldValue != newValue) {
  34.147 +            changeSupport.fireChange();
  34.148 +        }
  34.149 +    }
  34.150 +}
    35.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/pull/PullImageAction.java	Wed Mar 23 21:02:01 2016 +0000
    35.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/pull/PullImageAction.java	Thu Mar 24 15:34:03 2016 +0100
    35.3 @@ -59,7 +59,7 @@
    35.4  import org.netbeans.modules.docker.api.DockerAuthenticationException;
    35.5  import org.netbeans.modules.docker.api.DockerName;
    35.6  import org.netbeans.modules.docker.ui.credentials.CredentialsUtils;
    35.7 -import org.netbeans.modules.docker.ui.node.EnhancedDockerInstance;
    35.8 +import org.netbeans.modules.docker.ui.node.StatefulDockerInstance;
    35.9  import org.openide.DialogDescriptor;
   35.10  import org.openide.DialogDisplayer;
   35.11  import org.openide.NotifyDescriptor;
   35.12 @@ -123,7 +123,7 @@
   35.13          if (activatedNodes.length != 1) {
   35.14              return false;
   35.15          }
   35.16 -        EnhancedDockerInstance checked = activatedNodes[0].getLookup().lookup(EnhancedDockerInstance.class);
   35.17 +        StatefulDockerInstance checked = activatedNodes[0].getLookup().lookup(StatefulDockerInstance.class);
   35.18          if (checked == null || !checked.isAvailable()) {
   35.19              return false;
   35.20          }
    36.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/rename/RenameContainerAction.java	Wed Mar 23 21:02:01 2016 +0000
    36.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/rename/RenameContainerAction.java	Thu Mar 24 15:34:03 2016 +0100
    36.3 @@ -48,7 +48,7 @@
    36.4  import org.netbeans.api.progress.ProgressHandle;
    36.5  import org.netbeans.modules.docker.api.DockerException;
    36.6  import org.netbeans.modules.docker.api.DockerAction;
    36.7 -import org.netbeans.modules.docker.ui.node.EnhancedDockerContainer;
    36.8 +import org.netbeans.modules.docker.ui.node.StatefulDockerContainer;
    36.9  import org.openide.DialogDescriptor;
   36.10  import org.openide.DialogDisplayer;
   36.11  import org.openide.NotifyDescriptor;
   36.12 @@ -74,7 +74,7 @@
   36.13      })
   36.14      @Override
   36.15      protected void performAction(Node[] activatedNodes) {
   36.16 -        EnhancedDockerContainer container = activatedNodes[0].getLookup().lookup(EnhancedDockerContainer.class);
   36.17 +        StatefulDockerContainer container = activatedNodes[0].getLookup().lookup(StatefulDockerContainer.class);
   36.18          if (container != null) {
   36.19              JButton renameButton = new JButton();
   36.20              Mnemonics.setLocalizedText(renameButton, Bundle.LBL_Rename());
   36.21 @@ -107,7 +107,7 @@
   36.22          "# {0} - container name",
   36.23          "MSG_Renaming=Renaming {0}"
   36.24      })
   36.25 -    private void perform(final EnhancedDockerContainer container, final String name) {
   36.26 +    private void perform(final StatefulDockerContainer container, final String name) {
   36.27          RequestProcessor.getDefault().post(new Runnable() {
   36.28              @Override
   36.29              public void run() {
   36.30 @@ -133,7 +133,7 @@
   36.31          if (activatedNodes.length != 1) {
   36.32              return false;
   36.33          }
   36.34 -        return activatedNodes[0].getLookup().lookup(EnhancedDockerContainer.class) != null;
   36.35 +        return activatedNodes[0].getLookup().lookup(StatefulDockerContainer.class) != null;
   36.36      }
   36.37  
   36.38      @NbBundle.Messages("LBL_RenameContainerAction=Rename...")
    37.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/wizard/AddDockerInstanceWizard.java	Wed Mar 23 21:02:01 2016 +0000
    37.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/wizard/AddDockerInstanceWizard.java	Thu Mar 24 15:34:03 2016 +0100
    37.3 @@ -42,15 +42,19 @@
    37.4  package org.netbeans.modules.docker.ui.wizard;
    37.5  
    37.6  import java.io.File;
    37.7 +import java.net.MalformedURLException;
    37.8  import java.text.MessageFormat;
    37.9  import java.util.ArrayList;
   37.10  import java.util.List;
   37.11 +import java.util.logging.Level;
   37.12 +import java.util.logging.Logger;
   37.13  import javax.swing.JComponent;
   37.14  import org.netbeans.modules.docker.api.DockerInstance;
   37.15 -import org.netbeans.modules.docker.api.DockerIntegration;
   37.16 +import org.netbeans.modules.docker.api.DockerSupport;
   37.17  import org.openide.DialogDisplayer;
   37.18  import org.openide.WizardDescriptor;
   37.19  import org.openide.util.NbBundle;
   37.20 +import org.openide.util.Utilities;
   37.21  
   37.22  /**
   37.23   *
   37.24 @@ -60,6 +64,10 @@
   37.25  
   37.26      public static final String DISPLAY_NAME_PROPERTY = "displayName";
   37.27  
   37.28 +    public static final String SOCKET_SELECTED_PROPERTY = "socketSelected";
   37.29 +
   37.30 +    public static final String SOCKET_PROPERTY = "socket";
   37.31 +
   37.32      public static final String URL_PROPERTY = "url";
   37.33  
   37.34      public static final String CERTIFICATE_PATH_PROPERTY = "certPath";
   37.35 @@ -70,6 +78,8 @@
   37.36  
   37.37      public static final String DEFAULT_KEY_FILE = "key.pem";
   37.38  
   37.39 +    private static final Logger LOGGER = Logger.getLogger(AddDockerInstanceWizard.class.getName());
   37.40 +
   37.41      @NbBundle.Messages("LBL_AddDockerInstance=Add Docker Instance")
   37.42      public DockerInstance show() {
   37.43          List<WizardDescriptor.Panel<WizardDescriptor>> panels = new ArrayList<>();
   37.44 @@ -91,20 +101,37 @@
   37.45          wiz.setTitle(Bundle.LBL_AddDockerInstance());
   37.46          if (DialogDisplayer.getDefault().notify(wiz) == WizardDescriptor.FINISH_OPTION) {
   37.47  
   37.48 -            File caFile = null;
   37.49 -            File certFile = null;
   37.50 -            File keyFile = null;
   37.51 +            Boolean socketSelected = (Boolean) wiz.getProperty(SOCKET_SELECTED_PROPERTY);
   37.52 +            if (socketSelected) {
   37.53 +                File file = (File) wiz.getProperty(SOCKET_PROPERTY);
   37.54 +                try {
   37.55 +                    DockerInstance instance = DockerInstance.getInstance(
   37.56 +                            Utilities.toURI(file).toURL().toString(),
   37.57 +                            (String) wiz.getProperty(DISPLAY_NAME_PROPERTY),
   37.58 +                            null, null, null);
   37.59 +                    return DockerSupport.getDefault().addInstance(instance);
   37.60 +                } catch (MalformedURLException ex) {
   37.61 +                    LOGGER.log(Level.WARNING, null, ex);
   37.62 +                }
   37.63 +            } else {
   37.64 +                File caFile = null;
   37.65 +                File certFile = null;
   37.66 +                File keyFile = null;
   37.67  
   37.68 -            String strCertPath = (String) wiz.getProperty(CERTIFICATE_PATH_PROPERTY);
   37.69 -            if (strCertPath != null) {
   37.70 -                File file = new File(strCertPath);
   37.71 -                caFile = new File(file, DEFAULT_CA_FILE);
   37.72 -                certFile = new File(file, DEFAULT_CERT_FILE);
   37.73 -                keyFile = new File(file, DEFAULT_KEY_FILE);
   37.74 +                String strCertPath = (String) wiz.getProperty(CERTIFICATE_PATH_PROPERTY);
   37.75 +                if (strCertPath != null) {
   37.76 +                    File file = new File(strCertPath);
   37.77 +                    caFile = new File(file, DEFAULT_CA_FILE);
   37.78 +                    certFile = new File(file, DEFAULT_CERT_FILE);
   37.79 +                    keyFile = new File(file, DEFAULT_KEY_FILE);
   37.80 +                }
   37.81 +
   37.82 +                DockerInstance instance = DockerInstance.getInstance(
   37.83 +                        (String) wiz.getProperty(URL_PROPERTY),
   37.84 +                        (String) wiz.getProperty(DISPLAY_NAME_PROPERTY),
   37.85 +                        caFile, certFile, keyFile);
   37.86 +                return DockerSupport.getDefault().addInstance(instance);
   37.87              }
   37.88 -
   37.89 -            return DockerIntegration.getDefault().createInstance((String) wiz.getProperty(DISPLAY_NAME_PROPERTY),
   37.90 -                    (String) wiz.getProperty(URL_PROPERTY), caFile, certFile, keyFile);
   37.91          }
   37.92          return null;
   37.93      }
    38.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/wizard/Bundle.properties	Wed Mar 23 21:02:01 2016 +0000
    38.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/wizard/Bundle.properties	Thu Mar 24 15:34:03 2016 +0100
    38.3 @@ -1,7 +1,15 @@
    38.4  
    38.5  LBL_AddDockerInstanceAction=Add Docker...
    38.6 -DockerConnectionVisual.browseButton.text=&Browse...
    38.7 -DockerConnectionVisual.certDirectoryLabel.text=&Cerificates Path:
    38.8 -DockerConnectionVisual.nameLabel.text=&Name:
    38.9 -DockerConnectionVisual.explanationLabel.text=<html>The docker deamon must be configured to listen on TCP socket. You can do so by starting it with -H tcp://127.0.0.1:2375. See the Docker documentation for reference.</html>
   38.10 -DockerConnectionVisual.urlLabel.text=&URL:
   38.11 +ConfigurationPanel.nameLabel.text=&Name:
   38.12 +ConfigurationPanel.browseButton.text=&Browse...
   38.13 +ConfigurationPanel.certDirectoryLabel.text=&Cerificates Path:
   38.14 +ConfigurationPanel.urlLabel.text=&URL:
   38.15 +ConfigurationLinuxPanel.nameLabel.text=&Name:
   38.16 +ConfigurationLinuxPanel.certDirectoryLabel.text=&Cerificates Path:
   38.17 +ConfigurationLinuxPanel.urlLabel.text=&URL:
   38.18 +ConfigurationLinuxPanel.socketBrowseButton.text=B&rowse...
   38.19 +ConfigurationLinuxPanel.socketLabel.text=&Socket:
   38.20 +ConfigurationLinuxPanel.socketRadioButton.text=&Unix Socket:
   38.21 +ConfigurationLinuxPanel.urlRadioButton.text=&TCP Connection
   38.22 +ConfigurationLinuxPanel.certBrowseButton.text=&Browse...
   38.23 +DockerConnectionVisual.testButton.text=&Test Connection
    39.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    39.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/wizard/Configuration.java	Thu Mar 24 15:34:03 2016 +0100
    39.3 @@ -0,0 +1,76 @@
    39.4 +/*
    39.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    39.6 + *
    39.7 + * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
    39.8 + *
    39.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   39.10 + * Other names may be trademarks of their respective owners.
   39.11 + *
   39.12 + * The contents of this file are subject to the terms of either the GNU
   39.13 + * General Public License Version 2 only ("GPL") or the Common
   39.14 + * Development and Distribution License("CDDL") (collectively, the
   39.15 + * "License"). You may not use this file except in compliance with the
   39.16 + * License. You can obtain a copy of the License at
   39.17 + * http://www.netbeans.org/cddl-gplv2.html
   39.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   39.19 + * specific language governing permissions and limitations under the
   39.20 + * License.  When distributing the software, include this License Header
   39.21 + * Notice in each file and include the License file at
   39.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   39.23 + * particular file as subject to the "Classpath" exception as provided
   39.24 + * by Oracle in the GPL Version 2 section of the License file that
   39.25 + * accompanied this code. If applicable, add the following below the
   39.26 + * License Header, with the fields enclosed by brackets [] replaced by
   39.27 + * your own identifying information:
   39.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   39.29 + *
   39.30 + * If you wish your version of this file to be governed by only the CDDL
   39.31 + * or only the GPL Version 2, indicate your decision by adding
   39.32 + * "[Contributor] elects to include this software in this distribution
   39.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   39.34 + * single choice of license, a recipient has the option to distribute
   39.35 + * your version of this file under either the CDDL, the GPL Version 2 or
   39.36 + * to extend the choice of license to its licensees as provided above.
   39.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   39.38 + * Version 2 license, then the option applies only if the new code is
   39.39 + * made subject to such option by the copyright holder.
   39.40 + *
   39.41 + * Contributor(s):
   39.42 + *
   39.43 + * Portions Copyrighted 2016 Sun Microsystems, Inc.
   39.44 + */
   39.45 +package org.netbeans.modules.docker.ui.wizard;
   39.46 +
   39.47 +import java.io.File;
   39.48 +import javax.swing.event.ChangeListener;
   39.49 +
   39.50 +/**
   39.51 + *
   39.52 + * @author Petr Hejl
   39.53 + */
   39.54 +public interface Configuration {
   39.55 +
   39.56 +    void addChangeListener(ChangeListener l);
   39.57 +
   39.58 +    void removeChangeListener(ChangeListener l);
   39.59 +
   39.60 +    String getDisplayName();
   39.61 +
   39.62 +    void setDisplayName(String displayName);
   39.63 +
   39.64 +    boolean isSocketSelected();
   39.65 +
   39.66 +    void setSocketSelected(boolean socketSelected);
   39.67 +
   39.68 +    File getSocket();
   39.69 +
   39.70 +    void setSocket(File socket);
   39.71 +
   39.72 +    String getUrl();
   39.73 +
   39.74 +    void setUrl(String url);
   39.75 +
   39.76 +    String getCertPath();
   39.77 +
   39.78 +    void setCertPath(String path);
   39.79 +}
    40.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    40.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/wizard/ConfigurationLinuxPanel.form	Thu Mar 24 15:34:03 2016 +0100
    40.3 @@ -0,0 +1,180 @@
    40.4 +<?xml version="1.0" encoding="UTF-8" ?>
    40.5 +
    40.6 +<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
    40.7 +  <NonVisualComponents>
    40.8 +    <Component class="javax.swing.ButtonGroup" name="switchButtonGroup">
    40.9 +    </Component>
   40.10 +  </NonVisualComponents>
   40.11 +  <AuxValues>
   40.12 +    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
   40.13 +    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
   40.14 +    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
   40.15 +    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
   40.16 +    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
   40.17 +    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
   40.18 +    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
   40.19 +    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
   40.20 +    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
   40.21 +  </AuxValues>
   40.22 +
   40.23 +  <Layout>
   40.24 +    <DimensionLayout dim="0">
   40.25 +      <Group type="103" groupAlignment="0" attributes="0">
   40.26 +          <Group type="102" attributes="0">
   40.27 +              <Component id="nameLabel" min="-2" max="-2" attributes="0"/>
   40.28 +              <EmptySpace max="-2" attributes="0"/>
   40.29 +              <Component id="nameTextField" max="32767" attributes="0"/>
   40.30 +          </Group>
   40.31 +          <Group type="102" attributes="0">
   40.32 +              <Component id="socketRadioButton" min="-2" max="-2" attributes="0"/>
   40.33 +              <EmptySpace max="32767" attributes="0"/>
   40.34 +          </Group>
   40.35 +          <Group type="102" attributes="0">
   40.36 +              <Component id="urlRadioButton" min="-2" max="-2" attributes="0"/>
   40.37 +              <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
   40.38 +          </Group>
   40.39 +          <Group type="102" alignment="0" attributes="0">
   40.40 +              <EmptySpace max="-2" attributes="0"/>
   40.41 +              <Group type="103" groupAlignment="0" attributes="0">
   40.42 +                  <Component id="socketLabel" alignment="0" min="-2" max="-2" attributes="0"/>
   40.43 +                  <Component id="urlLabel" alignment="0" min="-2" max="-2" attributes="0"/>
   40.44 +                  <Component id="certDirectoryLabel" alignment="0" min="-2" max="-2" attributes="0"/>
   40.45 +              </Group>
   40.46 +              <EmptySpace max="-2" attributes="0"/>
   40.47 +              <Group type="103" groupAlignment="0" attributes="0">
   40.48 +                  <Group type="102" alignment="0" attributes="0">
   40.49 +                      <Component id="socketTextField" pref="190" max="32767" attributes="0"/>
   40.50 +                      <EmptySpace max="-2" attributes="0"/>
   40.51 +                      <Component id="socketBrowseButton" min="-2" max="-2" attributes="0"/>
   40.52 +                  </Group>
   40.53 +                  <Group type="102" alignment="0" attributes="0">
   40.54 +                      <Component id="certTextField" max="32767" attributes="0"/>
   40.55 +                      <EmptySpace max="-2" attributes="0"/>
   40.56 +                      <Component id="certBrowseButton" min="-2" max="-2" attributes="0"/>
   40.57 +                  </Group>
   40.58 +                  <Component id="urlTextField" alignment="0" max="32767" attributes="0"/>
   40.59 +              </Group>
   40.60 +          </Group>
   40.61 +      </Group>
   40.62 +    </DimensionLayout>
   40.63 +    <DimensionLayout dim="1">
   40.64 +      <Group type="103" groupAlignment="0" attributes="0">
   40.65 +          <Group type="102" alignment="0" attributes="0">
   40.66 +              <Group type="103" groupAlignment="3" attributes="0">
   40.67 +                  <Component id="nameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
   40.68 +                  <Component id="nameTextField" alignment="3" min="-2" max="-2" attributes="0"/>
   40.69 +              </Group>
   40.70 +              <EmptySpace max="-2" attributes="0"/>
   40.71 +              <Component id="socketRadioButton" min="-2" max="-2" attributes="0"/>
   40.72 +              <EmptySpace max="-2" attributes="0"/>
   40.73 +              <Group type="103" groupAlignment="3" attributes="0">
   40.74 +                  <Component id="socketLabel" alignment="3" min="-2" max="-2" attributes="0"/>
   40.75 +                  <Component id="socketTextField" alignment="3" min="-2" max="-2" attributes="0"/>
   40.76 +                  <Component id="socketBrowseButton" alignment="3" min="-2" max="-2" attributes="0"/>
   40.77 +              </Group>
   40.78 +              <EmptySpace max="-2" attributes="0"/>
   40.79 +              <Component id="urlRadioButton" min="-2" max="-2" attributes="0"/>
   40.80 +              <EmptySpace max="-2" attributes="0"/>
   40.81 +              <Group type="103" groupAlignment="3" attributes="0">
   40.82 +                  <Component id="urlLabel" alignment="3" min="-2" max="-2" attributes="0"/>
   40.83 +                  <Component id="urlTextField" alignment="3" min="-2" max="-2" attributes="0"/>
   40.84 +              </Group>
   40.85 +              <EmptySpace max="-2" attributes="0"/>
   40.86 +              <Group type="103" groupAlignment="3" attributes="0">
   40.87 +                  <Component id="certDirectoryLabel" alignment="3" min="-2" max="-2" attributes="0"/>
   40.88 +                  <Component id="certTextField" alignment="3" min="-2" max="-2" attributes="0"/>
   40.89 +                  <Component id="certBrowseButton" alignment="3" min="-2" max="-2" attributes="0"/>
   40.90 +              </Group>
   40.91 +          </Group>
   40.92 +      </Group>
   40.93 +    </DimensionLayout>
   40.94 +  </Layout>
   40.95 +  <SubComponents>
   40.96 +    <Component class="javax.swing.JLabel" name="nameLabel">
   40.97 +      <Properties>
   40.98 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   40.99 +          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="ConfigurationLinuxPanel.nameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  40.100 +        </Property>
  40.101 +      </Properties>
  40.102 +    </Component>
  40.103 +    <Component class="javax.swing.JTextField" name="nameTextField">
  40.104 +    </Component>
  40.105 +    <Component class="javax.swing.JLabel" name="urlLabel">
  40.106 +      <Properties>
  40.107 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  40.108 +          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="ConfigurationLinuxPanel.urlLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  40.109 +        </Property>
  40.110 +      </Properties>
  40.111 +    </Component>
  40.112 +    <Component class="javax.swing.JTextField" name="urlTextField">
  40.113 +    </Component>
  40.114 +    <Component class="javax.swing.JLabel" name="certDirectoryLabel">
  40.115 +      <Properties>
  40.116 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  40.117 +          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="ConfigurationLinuxPanel.certDirectoryLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  40.118 +        </Property>
  40.119 +      </Properties>
  40.120 +    </Component>
  40.121 +    <Component class="javax.swing.JTextField" name="certTextField">
  40.122 +    </Component>
  40.123 +    <Component class="javax.swing.JButton" name="certBrowseButton">
  40.124 +      <Properties>
  40.125 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  40.126 +          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="ConfigurationLinuxPanel.certBrowseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  40.127 +        </Property>
  40.128 +      </Properties>
  40.129 +      <Events>
  40.130 +        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="certBrowseButtonActionPerformed"/>
  40.131 +      </Events>
  40.132 +    </Component>
  40.133 +    <Component class="javax.swing.JRadioButton" name="socketRadioButton">
  40.134 +      <Properties>
  40.135 +        <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
  40.136 +          <ComponentRef name="switchButtonGroup"/>
  40.137 +        </Property>
  40.138 +        <Property name="selected" type="boolean" value="true"/>
  40.139 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  40.140 +          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="ConfigurationLinuxPanel.socketRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  40.141 +        </Property>
  40.142 +      </Properties>
  40.143 +      <Events>
  40.144 +        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="socketRadioButtonActionPerformed"/>
  40.145 +      </Events>
  40.146 +    </Component>
  40.147 +    <Component class="javax.swing.JLabel" name="socketLabel">
  40.148 +      <Properties>
  40.149 +        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
  40.150 +          <ComponentRef name="socketTextField"/>
  40.151 +        </Property>
  40.152 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  40.153 +          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="ConfigurationLinuxPanel.socketLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  40.154 +        </Property>
  40.155 +      </Properties>
  40.156 +    </Component>
  40.157 +    <Component class="javax.swing.JButton" name="socketBrowseButton">
  40.158 +      <Properties>
  40.159 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  40.160 +          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="ConfigurationLinuxPanel.socketBrowseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  40.161 +        </Property>
  40.162 +      </Properties>
  40.163 +      <Events>
  40.164 +        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="socketBrowseButtonActionPerformed"/>
  40.165 +      </Events>
  40.166 +    </Component>
  40.167 +    <Component class="javax.swing.JTextField" name="socketTextField">
  40.168 +    </Component>
  40.169 +    <Component class="javax.swing.JRadioButton" name="urlRadioButton">
  40.170 +      <Properties>
  40.171 +        <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
  40.172 +          <ComponentRef name="switchButtonGroup"/>
  40.173 +        </Property>
  40.174 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  40.175 +          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="ConfigurationLinuxPanel.urlRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  40.176 +        </Property>
  40.177 +      </Properties>
  40.178 +      <Events>
  40.179 +        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="urlRadioButtonActionPerformed"/>
  40.180 +      </Events>
  40.181 +    </Component>
  40.182 +  </SubComponents>
  40.183 +</Form>
    41.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    41.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/wizard/ConfigurationLinuxPanel.java	Thu Mar 24 15:34:03 2016 +0100
    41.3 @@ -0,0 +1,342 @@
    41.4 +/*
    41.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    41.6 + *
    41.7 + * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
    41.8 + *
    41.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   41.10 + * Other names may be trademarks of their respective owners.
   41.11 + *
   41.12 + * The contents of this file are subject to the terms of either the GNU
   41.13 + * General Public License Version 2 only ("GPL") or the Common
   41.14 + * Development and Distribution License("CDDL") (collectively, the
   41.15 + * "License"). You may not use this file except in compliance with the
   41.16 + * License. You can obtain a copy of the License at
   41.17 + * http://www.netbeans.org/cddl-gplv2.html
   41.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   41.19 + * specific language governing permissions and limitations under the
   41.20 + * License.  When distributing the software, include this License Header
   41.21 + * Notice in each file and include the License file at
   41.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   41.23 + * particular file as subject to the "Classpath" exception as provided
   41.24 + * by Oracle in the GPL Version 2 section of the License file that
   41.25 + * accompanied this code. If applicable, add the following below the
   41.26 + * License Header, with the fields enclosed by brackets [] replaced by
   41.27 + * your own identifying information:
   41.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   41.29 + *
   41.30 + * If you wish your version of this file to be governed by only the CDDL
   41.31 + * or only the GPL Version 2, indicate your decision by adding
   41.32 + * "[Contributor] elects to include this software in this distribution
   41.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   41.34 + * single choice of license, a recipient has the option to distribute
   41.35 + * your version of this file under either the CDDL, the GPL Version 2 or
   41.36 + * to extend the choice of license to its licensees as provided above.
   41.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   41.38 + * Version 2 license, then the option applies only if the new code is
   41.39 + * made subject to such option by the copyright holder.
   41.40 + *
   41.41 + * Contributor(s):
   41.42 + *
   41.43 + * Portions Copyrighted 2016 Sun Microsystems, Inc.
   41.44 + */
   41.45 +package org.netbeans.modules.docker.ui.wizard;
   41.46 +
   41.47 +import java.io.File;
   41.48 +import javax.swing.JFileChooser;
   41.49 +import javax.swing.SwingUtilities;
   41.50 +import javax.swing.event.ChangeListener;
   41.51 +import javax.swing.event.DocumentEvent;
   41.52 +import javax.swing.event.DocumentListener;
   41.53 +import org.netbeans.modules.docker.ui.UiUtils;
   41.54 +import org.openide.util.ChangeSupport;
   41.55 +
   41.56 +/**
   41.57 + *
   41.58 + * @author Petr Hejl
   41.59 + */
   41.60 +public class ConfigurationLinuxPanel extends javax.swing.JPanel implements Configuration {
   41.61 +
   41.62 +    private final ChangeSupport changeSupport = new ChangeSupport(this);
   41.63 +
   41.64 +    /**
   41.65 +     * Creates new form DockerConnectionLinux
   41.66 +     */
   41.67 +    public ConfigurationLinuxPanel() {
   41.68 +        initComponents();
   41.69 +
   41.70 +        DefaultDocumentListener listener = new DefaultDocumentListener();
   41.71 +        nameTextField.getDocument().addDocumentListener(listener);
   41.72 +        socketTextField.getDocument().addDocumentListener(listener);
   41.73 +        urlTextField.getDocument().addDocumentListener(listener);
   41.74 +        certTextField.getDocument().addDocumentListener(listener);
   41.75 +    }
   41.76 +
   41.77 +    @Override
   41.78 +    public void addChangeListener(ChangeListener l) {
   41.79 +        changeSupport.addChangeListener(l);
   41.80 +    }
   41.81 +
   41.82 +    @Override
   41.83 +    public void removeChangeListener(ChangeListener l) {
   41.84 +        changeSupport.removeChangeListener(l);
   41.85 +    }
   41.86 +
   41.87 +    @Override
   41.88 +    public String getDisplayName() {
   41.89 +        return UiUtils.getValue(nameTextField);
   41.90 +    }
   41.91 +
   41.92 +    @Override
   41.93 +    public void setDisplayName(String displayName) {
   41.94 +        nameTextField.setText(displayName);
   41.95 +    }
   41.96 +
   41.97 +    @Override
   41.98 +    public boolean isSocketSelected() {
   41.99 +        return socketRadioButton.isSelected();
  41.100 +    }
  41.101 +
  41.102 +    @Override
  41.103 +    public void setSocketSelected(boolean socketSelected) {
  41.104 +        socketRadioButton.setSelected(socketSelected);
  41.105 +        refresh();
  41.106 +    }
  41.107 +
  41.108 +    @Override
  41.109 +    public File getSocket() {
  41.110 +        String value = UiUtils.getValue(socketTextField);
  41.111 +        if (value != null) {
  41.112 +            return new File(value);
  41.113 +        }
  41.114 +        return null;
  41.115 +    }
  41.116 +
  41.117 +    @Override
  41.118 +    public void setSocket(File socket) {
  41.119 +        if (socket != null) {
  41.120 +            socketTextField.setText(socket.getAbsolutePath());
  41.121 +        }
  41.122 +    }
  41.123 +
  41.124 +    @Override
  41.125 +    public String getUrl() {
  41.126 +        return UiUtils.getValue(urlTextField);
  41.127 +    }
  41.128 +
  41.129 +    @Override
  41.130 +    public void setUrl(String url) {
  41.131 +        urlTextField.setText(url);
  41.132 +    }
  41.133 +
  41.134 +    @Override
  41.135 +    public String getCertPath() {
  41.136 +        return UiUtils.getValue(certTextField);
  41.137 +    }
  41.138 +
  41.139 +    @Override
  41.140 +    public void setCertPath(String path) {
  41.141 +        certTextField.setText(path);
  41.142 +    }
  41.143 +
  41.144 +    private void refresh() {
  41.145 +        boolean socketSelected = socketRadioButton.isSelected();
  41.146 +        socketTextField.setEnabled(socketSelected);
  41.147 +        socketBrowseButton.setEnabled(socketSelected);
  41.148 +        urlTextField.setEnabled(!socketSelected);
  41.149 +        certTextField.setEnabled(!socketSelected);
  41.150 +        certBrowseButton.setEnabled(!socketSelected);
  41.151 +
  41.152 +        changeSupport.fireChange();
  41.153 +    }
  41.154 +
  41.155 +    private class DefaultDocumentListener implements DocumentListener {
  41.156 +
  41.157 +        @Override
  41.158 +        public void insertUpdate(DocumentEvent e) {
  41.159 +            changeSupport.fireChange();
  41.160 +        }
  41.161 +
  41.162 +        @Override
  41.163 +        public void removeUpdate(DocumentEvent e) {
  41.164 +            changeSupport.fireChange();
  41.165 +        }
  41.166 +
  41.167 +        @Override
  41.168 +        public void changedUpdate(DocumentEvent e) {
  41.169 +            changeSupport.fireChange();
  41.170 +        }
  41.171 +    }
  41.172 +
  41.173 +    /**
  41.174 +     * This method is called from within the constructor to initialize the form.
  41.175 +     * WARNING: Do NOT modify this code. The content of this method is always
  41.176 +     * regenerated by the Form Editor.
  41.177 +     */
  41.178 +    @SuppressWarnings("unchecked")
  41.179 +    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  41.180 +    private void initComponents() {
  41.181 +
  41.182 +        switchButtonGroup = new javax.swing.ButtonGroup();
  41.183 +        nameLabel = new javax.swing.JLabel();
  41.184 +        nameTextField = new javax.swing.JTextField();
  41.185 +        urlLabel = new javax.swing.JLabel();
  41.186 +        urlTextField = new javax.swing.JTextField();
  41.187 +        certDirectoryLabel = new javax.swing.JLabel();
  41.188 +        certTextField = new javax.swing.JTextField();
  41.189 +        certBrowseButton = new javax.swing.JButton();
  41.190 +        socketRadioButton = new javax.swing.JRadioButton();
  41.191 +        socketLabel = new javax.swing.JLabel();
  41.192 +        socketBrowseButton = new javax.swing.JButton();
  41.193 +        socketTextField = new javax.swing.JTextField();
  41.194 +        urlRadioButton = new javax.swing.JRadioButton();
  41.195 +
  41.196 +        org.openide.awt.Mnemonics.setLocalizedText(nameLabel, org.openide.util.NbBundle.getMessage(ConfigurationLinuxPanel.class, "ConfigurationLinuxPanel.nameLabel.text")); // NOI18N
  41.197 +
  41.198 +        org.openide.awt.Mnemonics.setLocalizedText(urlLabel, org.openide.util.NbBundle.getMessage(ConfigurationLinuxPanel.class, "ConfigurationLinuxPanel.urlLabel.text")); // NOI18N
  41.199 +
  41.200 +        org.openide.awt.Mnemonics.setLocalizedText(certDirectoryLabel, org.openide.util.NbBundle.getMessage(ConfigurationLinuxPanel.class, "ConfigurationLinuxPanel.certDirectoryLabel.text")); // NOI18N
  41.201 +
  41.202 +        org.openide.awt.Mnemonics.setLocalizedText(certBrowseButton, org.openide.util.NbBundle.getMessage(ConfigurationLinuxPanel.class, "ConfigurationLinuxPanel.certBrowseButton.text")); // NOI18N
  41.203 +        certBrowseButton.addActionListener(new java.awt.event.ActionListener() {
  41.204 +            public void actionPerformed(java.awt.event.ActionEvent evt) {
  41.205 +                certBrowseButtonActionPerformed(evt);
  41.206 +            }
  41.207 +        });
  41.208 +
  41.209 +        switchButtonGroup.add(socketRadioButton);
  41.210 +        socketRadioButton.setSelected(true);
  41.211 +        org.openide.awt.Mnemonics.setLocalizedText(socketRadioButton, org.openide.util.NbBundle.getMessage(ConfigurationLinuxPanel.class, "ConfigurationLinuxPanel.socketRadioButton.text")); // NOI18N
  41.212 +        socketRadioButton.addActionListener(new java.awt.event.ActionListener() {
  41.213 +            public void actionPerformed(java.awt.event.ActionEvent evt) {
  41.214 +                socketRadioButtonActionPerformed(evt);
  41.215 +            }
  41.216 +        });
  41.217 +
  41.218 +        socketLabel.setLabelFor(socketTextField);
  41.219 +        org.openide.awt.Mnemonics.setLocalizedText(socketLabel, org.openide.util.NbBundle.getMessage(ConfigurationLinuxPanel.class, "ConfigurationLinuxPanel.socketLabel.text")); // NOI18N
  41.220 +
  41.221 +        org.openide.awt.Mnemonics.setLocalizedText(socketBrowseButton, org.openide.util.NbBundle.getMessage(ConfigurationLinuxPanel.class, "ConfigurationLinuxPanel.socketBrowseButton.text")); // NOI18N
  41.222 +        socketBrowseButton.addActionListener(new java.awt.event.ActionListener() {
  41.223 +            public void actionPerformed(java.awt.event.ActionEvent evt) {
  41.224 +                socketBrowseButtonActionPerformed(evt);
  41.225 +            }
  41.226 +        });
  41.227 +
  41.228 +        switchButtonGroup.add(urlRadioButton);
  41.229 +        org.openide.awt.Mnemonics.setLocalizedText(urlRadioButton, org.openide.util.NbBundle.getMessage(ConfigurationLinuxPanel.class, "ConfigurationLinuxPanel.urlRadioButton.text")); // NOI18N
  41.230 +        urlRadioButton.addActionListener(new java.awt.event.ActionListener() {
  41.231 +            public void actionPerformed(java.awt.event.ActionEvent evt) {
  41.232 +                urlRadioButtonActionPerformed(evt);
  41.233 +            }
  41.234 +        });
  41.235 +
  41.236 +        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
  41.237 +        this.setLayout(layout);
  41.238 +        layout.setHorizontalGroup(
  41.239 +            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  41.240 +            .addGroup(layout.createSequentialGroup()
  41.241 +                .addComponent(nameLabel)
  41.242 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  41.243 +                .addComponent(nameTextField))
  41.244 +            .addGroup(layout.createSequentialGroup()
  41.245 +                .addComponent(socketRadioButton)
  41.246 +                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
  41.247 +            .addGroup(layout.createSequentialGroup()
  41.248 +                .addComponent(urlRadioButton)
  41.249 +                .addGap(0, 0, Short.MAX_VALUE))
  41.250 +            .addGroup(layout.createSequentialGroup()
  41.251 +                .addContainerGap()
  41.252 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  41.253 +                    .addComponent(socketLabel)
  41.254 +                    .addComponent(urlLabel)
  41.255 +                    .addComponent(certDirectoryLabel))
  41.256 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  41.257 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  41.258 +                    .addGroup(layout.createSequentialGroup()
  41.259 +                        .addComponent(socketTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 190, Short.MAX_VALUE)
  41.260 +                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  41.261 +                        .addComponent(socketBrowseButton))
  41.262 +                    .addGroup(layout.createSequentialGroup()
  41.263 +                        .addComponent(certTextField)
  41.264 +                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  41.265 +                        .addComponent(certBrowseButton))
  41.266 +                    .addComponent(urlTextField)))
  41.267 +        );
  41.268 +        layout.setVerticalGroup(
  41.269 +            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  41.270 +            .addGroup(layout.createSequentialGroup()
  41.271 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
  41.272 +                    .addComponent(nameLabel)
  41.273 +                    .addComponent(nameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
  41.274 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  41.275 +                .addComponent(socketRadioButton)
  41.276 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  41.277 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
  41.278 +                    .addComponent(socketLabel)
  41.279 +                    .addComponent(socketTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
  41.280 +                    .addComponent(socketBrowseButton))
  41.281 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  41.282 +                .addComponent(urlRadioButton)
  41.283 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  41.284 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
  41.285 +                    .addComponent(urlLabel)
  41.286 +                    .addComponent(urlTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
  41.287 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  41.288 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
  41.289 +                    .addComponent(certDirectoryLabel)
  41.290 +                    .addComponent(certTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
  41.291 +                    .addComponent(certBrowseButton)))
  41.292 +        );
  41.293 +    }// </editor-fold>//GEN-END:initComponents
  41.294 +
  41.295 +    private void certBrowseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_certBrowseButtonActionPerformed
  41.296 +        JFileChooser chooser = new JFileChooser();
  41.297 +        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
  41.298 +        chooser.setFileHidingEnabled(false);
  41.299 +        String text = UiUtils.getValue(certTextField);
  41.300 +        if (text != null) {
  41.301 +            chooser.setSelectedFile(new File(text));
  41.302 +        }
  41.303 +        if (chooser.showOpenDialog(SwingUtilities.getWindowAncestor(this)) == JFileChooser.APPROVE_OPTION) {
  41.304 +            certTextField.setText(chooser.getSelectedFile().getAbsolutePath());
  41.305 +        }
  41.306 +    }//GEN-LAST:event_certBrowseButtonActionPerformed
  41.307 +
  41.308 +    private void socketBrowseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_socketBrowseButtonActionPerformed
  41.309 +        JFileChooser chooser = new JFileChooser();
  41.310 +        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
  41.311 +        chooser.setFileHidingEnabled(false);
  41.312 +        String text = UiUtils.getValue(socketTextField);
  41.313 +        if (text != null) {
  41.314 +            chooser.setSelectedFile(new File(text));
  41.315 +        }
  41.316 +        if (chooser.showOpenDialog(SwingUtilities.getWindowAncestor(this)) == JFileChooser.APPROVE_OPTION) {
  41.317 +            socketTextField.setText(chooser.getSelectedFile().getAbsolutePath());
  41.318 +        }
  41.319 +    }//GEN-LAST:event_socketBrowseButtonActionPerformed
  41.320 +
  41.321 +    private void socketRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_socketRadioButtonActionPerformed
  41.322 +        refresh();
  41.323 +    }//GEN-LAST:event_socketRadioButtonActionPerformed
  41.324 +
  41.325 +    private void urlRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_urlRadioButtonActionPerformed
  41.326 +        refresh();
  41.327 +    }//GEN-LAST:event_urlRadioButtonActionPerformed
  41.328 +
  41.329 +
  41.330 +    // Variables declaration - do not modify//GEN-BEGIN:variables
  41.331 +    private javax.swing.JButton certBrowseButton;
  41.332 +    private javax.swing.JLabel certDirectoryLabel;
  41.333 +    private javax.swing.JTextField certTextField;
  41.334 +    private javax.swing.JLabel nameLabel;
  41.335 +    private javax.swing.JTextField nameTextField;
  41.336 +    private javax.swing.JButton socketBrowseButton;
  41.337 +    private javax.swing.JLabel socketLabel;
  41.338 +    private javax.swing.JRadioButton socketRadioButton;
  41.339 +    private javax.swing.JTextField socketTextField;
  41.340 +    private javax.swing.ButtonGroup switchButtonGroup;
  41.341 +    private javax.swing.JLabel urlLabel;
  41.342 +    private javax.swing.JRadioButton urlRadioButton;
  41.343 +    private javax.swing.JTextField urlTextField;
  41.344 +    // End of variables declaration//GEN-END:variables
  41.345 +}
    42.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    42.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/wizard/ConfigurationPanel.form	Thu Mar 24 15:34:03 2016 +0100
    42.3 @@ -0,0 +1,99 @@
    42.4 +<?xml version="1.0" encoding="UTF-8" ?>
    42.5 +
    42.6 +<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
    42.7 +  <AuxValues>
    42.8 +    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
    42.9 +    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
   42.10 +    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
   42.11 +    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
   42.12 +    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
   42.13 +    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
   42.14 +    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
   42.15 +    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
   42.16 +    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
   42.17 +  </AuxValues>
   42.18 +
   42.19 +  <Layout>
   42.20 +    <DimensionLayout dim="0">
   42.21 +      <Group type="103" groupAlignment="0" attributes="0">
   42.22 +          <Group type="102" alignment="0" attributes="0">
   42.23 +              <Group type="103" groupAlignment="0" attributes="0">
   42.24 +                  <Component id="certDirectoryLabel" alignment="0" min="-2" max="-2" attributes="0"/>
   42.25 +                  <Component id="urlLabel" alignment="0" min="-2" max="-2" attributes="0"/>
   42.26 +                  <Component id="nameLabel" alignment="0" min="-2" max="-2" attributes="0"/>
   42.27 +              </Group>
   42.28 +              <EmptySpace max="-2" attributes="0"/>
   42.29 +              <Group type="103" groupAlignment="0" attributes="0">
   42.30 +                  <Group type="102" alignment="0" attributes="0">
   42.31 +                      <Component id="certTextField" pref="202" max="32767" attributes="0"/>
   42.32 +                      <EmptySpace max="-2" attributes="0"/>
   42.33 +                      <Component id="browseButton" min="-2" max="-2" attributes="0"/>
   42.34 +                  </Group>
   42.35 +                  <Component id="nameTextField" max="32767" attributes="0"/>
   42.36 +                  <Component id="urlTextField" alignment="0" max="32767" attributes="0"/>
   42.37 +              </Group>
   42.38 +          </Group>
   42.39 +      </Group>
   42.40 +    </DimensionLayout>
   42.41 +    <DimensionLayout dim="1">
   42.42 +      <Group type="103" groupAlignment="0" attributes="0">
   42.43 +          <Group type="102" alignment="0" attributes="0">
   42.44 +              <Group type="103" groupAlignment="3" attributes="0">
   42.45 +                  <Component id="nameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
   42.46 +                  <Component id="nameTextField" alignment="3" min="-2" max="-2" attributes="0"/>
   42.47 +              </Group>
   42.48 +              <EmptySpace max="-2" attributes="0"/>
   42.49 +              <Group type="103" groupAlignment="3" attributes="0">
   42.50 +                  <Component id="urlLabel" alignment="3" min="-2" max="-2" attributes="0"/>
   42.51 +                  <Component id="urlTextField" alignment="3" min="-2" max="-2" attributes="0"/>
   42.52 +              </Group>
   42.53 +              <EmptySpace max="-2" attributes="0"/>
   42.54 +              <Group type="103" groupAlignment="3" attributes="0">
   42.55 +                  <Component id="certDirectoryLabel" alignment="3" min="-2" max="-2" attributes="0"/>
   42.56 +                  <Component id="certTextField" alignment="3" min="-2" max="-2" attributes="0"/>
   42.57 +                  <Component id="browseButton" alignment="3" min="-2" max="-2" attributes="0"/>
   42.58 +              </Group>
   42.59 +          </Group>
   42.60 +      </Group>
   42.61 +    </DimensionLayout>
   42.62 +  </Layout>
   42.63 +  <SubComponents>
   42.64 +    <Component class="javax.swing.JLabel" name="nameLabel">
   42.65 +      <Properties>
   42.66 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   42.67 +          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="ConfigurationPanel.nameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
   42.68 +        </Property>
   42.69 +      </Properties>
   42.70 +    </Component>
   42.71 +    <Component class="javax.swing.JTextField" name="nameTextField">
   42.72 +    </Component>
   42.73 +    <Component class="javax.swing.JLabel" name="urlLabel">
   42.74 +      <Properties>
   42.75 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   42.76 +          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="ConfigurationPanel.urlLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
   42.77 +        </Property>
   42.78 +      </Properties>
   42.79 +    </Component>
   42.80 +    <Component class="javax.swing.JTextField" name="urlTextField">
   42.81 +    </Component>
   42.82 +    <Component class="javax.swing.JLabel" name="certDirectoryLabel">
   42.83 +      <Properties>
   42.84 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   42.85 +          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="ConfigurationPanel.certDirectoryLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
   42.86 +        </Property>
   42.87 +      </Properties>
   42.88 +    </Component>
   42.89 +    <Component class="javax.swing.JTextField" name="certTextField">
   42.90 +    </Component>
   42.91 +    <Component class="javax.swing.JButton" name="browseButton">
   42.92 +      <Properties>
   42.93 +        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   42.94 +          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="ConfigurationPanel.browseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
   42.95 +        </Property>
   42.96 +      </Properties>
   42.97 +      <Events>
   42.98 +        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseButtonActionPerformed"/>
   42.99 +      </Events>
  42.100 +    </Component>
  42.101 +  </SubComponents>
  42.102 +</Form>
    43.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    43.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/wizard/ConfigurationPanel.java	Thu Mar 24 15:34:03 2016 +0100
    43.3 @@ -0,0 +1,240 @@
    43.4 +/*
    43.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    43.6 + *
    43.7 + * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
    43.8 + *
    43.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   43.10 + * Other names may be trademarks of their respective owners.
   43.11 + *
   43.12 + * The contents of this file are subject to the terms of either the GNU
   43.13 + * General Public License Version 2 only ("GPL") or the Common
   43.14 + * Development and Distribution License("CDDL") (collectively, the
   43.15 + * "License"). You may not use this file except in compliance with the
   43.16 + * License. You can obtain a copy of the License at
   43.17 + * http://www.netbeans.org/cddl-gplv2.html
   43.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   43.19 + * specific language governing permissions and limitations under the
   43.20 + * License.  When distributing the software, include this License Header
   43.21 + * Notice in each file and include the License file at
   43.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   43.23 + * particular file as subject to the "Classpath" exception as provided
   43.24 + * by Oracle in the GPL Version 2 section of the License file that
   43.25 + * accompanied this code. If applicable, add the following below the
   43.26 + * License Header, with the fields enclosed by brackets [] replaced by
   43.27 + * your own identifying information:
   43.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   43.29 + *
   43.30 + * If you wish your version of this file to be governed by only the CDDL
   43.31 + * or only the GPL Version 2, indicate your decision by adding
   43.32 + * "[Contributor] elects to include this software in this distribution
   43.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   43.34 + * single choice of license, a recipient has the option to distribute
   43.35 + * your version of this file under either the CDDL, the GPL Version 2 or
   43.36 + * to extend the choice of license to its licensees as provided above.
   43.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   43.38 + * Version 2 license, then the option applies only if the new code is
   43.39 + * made subject to such option by the copyright holder.
   43.40 + *
   43.41 + * Contributor(s):
   43.42 + *
   43.43 + * Portions Copyrighted 2016 Sun Microsystems, Inc.
   43.44 + */
   43.45 +package org.netbeans.modules.docker.ui.wizard;
   43.46 +
   43.47 +import java.io.File;
   43.48 +import javax.swing.JFileChooser;
   43.49 +import javax.swing.SwingUtilities;
   43.50 +import javax.swing.event.ChangeListener;
   43.51 +import javax.swing.event.DocumentEvent;
   43.52 +import javax.swing.event.DocumentListener;
   43.53 +import org.netbeans.modules.docker.ui.UiUtils;
   43.54 +import org.openide.util.ChangeSupport;
   43.55 +
   43.56 +/**
   43.57 + *
   43.58 + * @author Petr Hejl
   43.59 + */
   43.60 +public class ConfigurationPanel extends javax.swing.JPanel implements Configuration {
   43.61 +
   43.62 +    private final ChangeSupport changeSupport = new ChangeSupport(this);
   43.63 +
   43.64 +    /**
   43.65 +     * Creates new form DockerConnectionGeneric
   43.66 +     */
   43.67 +    public ConfigurationPanel() {
   43.68 +        initComponents();
   43.69 +
   43.70 +        DefaultDocumentListener listener = new DefaultDocumentListener();
   43.71 +        nameTextField.getDocument().addDocumentListener(listener);
   43.72 +        urlTextField.getDocument().addDocumentListener(listener);
   43.73 +        certTextField.getDocument().addDocumentListener(listener);
   43.74 +    }
   43.75 +
   43.76 +    @Override
   43.77 +    public void addChangeListener(ChangeListener l) {
   43.78 +        changeSupport.addChangeListener(l);
   43.79 +    }
   43.80 +
   43.81 +    @Override
   43.82 +    public void removeChangeListener(ChangeListener l) {
   43.83 +        changeSupport.removeChangeListener(l);
   43.84 +    }
   43.85 +
   43.86 +    @Override
   43.87 +    public String getDisplayName() {
   43.88 +        return UiUtils.getValue(nameTextField);
   43.89 +    }
   43.90 +
   43.91 +    @Override
   43.92 +    public void setDisplayName(String displayName) {
   43.93 +        nameTextField.setText(displayName);
   43.94 +    }
   43.95 +
   43.96 +    @Override
   43.97 +    public boolean isSocketSelected() {
   43.98 +        return false;
   43.99 +    }
  43.100 +
  43.101 +    @Override
  43.102 +    public void setSocketSelected(boolean socketSelected) {
  43.103 +        // noop
  43.104 +    }
  43.105 +
  43.106 +    @Override
  43.107 +    public File getSocket() {
  43.108 +        return null;
  43.109 +    }
  43.110 +
  43.111 +    @Override
  43.112 +    public void setSocket(File socket) {
  43.113 +        // noop
  43.114 +    }
  43.115 +
  43.116 +    @Override
  43.117 +    public String getUrl() {
  43.118 +        return UiUtils.getValue(urlTextField);
  43.119 +    }
  43.120 +
  43.121 +    @Override
  43.122 +    public void setUrl(String url) {
  43.123 +        urlTextField.setText(url);
  43.124 +    }
  43.125 +
  43.126 +    @Override
  43.127 +    public String getCertPath() {
  43.128 +        return UiUtils.getValue(certTextField);
  43.129 +    }
  43.130 +
  43.131 +    @Override
  43.132 +    public void setCertPath(String path) {
  43.133 +        certTextField.setText(path);
  43.134 +    }
  43.135 +
  43.136 +    private class DefaultDocumentListener implements DocumentListener {
  43.137 +
  43.138 +        @Override
  43.139 +        public void insertUpdate(DocumentEvent e) {
  43.140 +            changeSupport.fireChange();
  43.141 +        }
  43.142 +
  43.143 +        @Override
  43.144 +        public void removeUpdate(DocumentEvent e) {
  43.145 +            changeSupport.fireChange();
  43.146 +        }
  43.147 +
  43.148 +        @Override
  43.149 +        public void changedUpdate(DocumentEvent e) {
  43.150 +            changeSupport.fireChange();
  43.151 +        }
  43.152 +    }
  43.153 +
  43.154 +    /**
  43.155 +     * This method is called from within the constructor to initialize the form.
  43.156 +     * WARNING: Do NOT modify this code. The content of this method is always
  43.157 +     * regenerated by the Form Editor.
  43.158 +     */
  43.159 +    @SuppressWarnings("unchecked")
  43.160 +    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  43.161 +    private void initComponents() {
  43.162 +
  43.163 +        nameLabel = new javax.swing.JLabel();
  43.164 +        nameTextField = new javax.swing.JTextField();
  43.165 +        urlLabel = new javax.swing.JLabel();
  43.166 +        urlTextField = new javax.swing.JTextField();
  43.167 +        certDirectoryLabel = new javax.swing.JLabel();
  43.168 +        certTextField = new javax.swing.JTextField();
  43.169 +        browseButton = new javax.swing.JButton();
  43.170 +
  43.171 +        org.openide.awt.Mnemonics.setLocalizedText(nameLabel, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.nameLabel.text")); // NOI18N
  43.172 +
  43.173 +        org.openide.awt.Mnemonics.setLocalizedText(urlLabel, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.urlLabel.text")); // NOI18N
  43.174 +
  43.175 +        org.openide.awt.Mnemonics.setLocalizedText(certDirectoryLabel, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.certDirectoryLabel.text")); // NOI18N
  43.176 +
  43.177 +        org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(ConfigurationPanel.class, "ConfigurationPanel.browseButton.text")); // NOI18N
  43.178 +        browseButton.addActionListener(new java.awt.event.ActionListener() {
  43.179 +            public void actionPerformed(java.awt.event.ActionEvent evt) {
  43.180 +                browseButtonActionPerformed(evt);
  43.181 +            }
  43.182 +        });
  43.183 +
  43.184 +        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
  43.185 +        this.setLayout(layout);
  43.186 +        layout.setHorizontalGroup(
  43.187 +            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  43.188 +            .addGroup(layout.createSequentialGroup()
  43.189 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  43.190 +                    .addComponent(certDirectoryLabel)
  43.191 +                    .addComponent(urlLabel)
  43.192 +                    .addComponent(nameLabel))
  43.193 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  43.194 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  43.195 +                    .addGroup(layout.createSequentialGroup()
  43.196 +                        .addComponent(certTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 202, Short.MAX_VALUE)
  43.197 +                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  43.198 +                        .addComponent(browseButton))
  43.199 +                    .addComponent(nameTextField)
  43.200 +                    .addComponent(urlTextField)))
  43.201 +        );
  43.202 +        layout.setVerticalGroup(
  43.203 +            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  43.204 +            .addGroup(layout.createSequentialGroup()
  43.205 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
  43.206 +                    .addComponent(nameLabel)
  43.207 +                    .addComponent(nameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
  43.208 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  43.209 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
  43.210 +                    .addComponent(urlLabel)
  43.211 +                    .addComponent(urlTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
  43.212 +                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  43.213 +                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
  43.214 +                    .addComponent(certDirectoryLabel)
  43.215 +                    .addComponent(certTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
  43.216 +                    .addComponent(browseButton)))
  43.217 +        );
  43.218 +    }// </editor-fold>//GEN-END:initComponents
  43.219 +
  43.220 +    private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
  43.221 +        JFileChooser chooser = new JFileChooser();
  43.222 +        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
  43.223 +        chooser.setFileHidingEnabled(false);
  43.224 +        String text = UiUtils.getValue(certTextField);
  43.225 +        if (text != null) {
  43.226 +            chooser.setSelectedFile(new File(text));
  43.227 +        }
  43.228 +        if (chooser.showOpenDialog(SwingUtilities.getWindowAncestor(this)) == JFileChooser.APPROVE_OPTION) {
  43.229 +            certTextField.setText(chooser.getSelectedFile().getAbsolutePath());
  43.230 +        }
  43.231 +    }//GEN-LAST:event_browseButtonActionPerformed
  43.232 +
  43.233 +
  43.234 +    // Variables declaration - do not modify//GEN-BEGIN:variables
  43.235 +    private javax.swing.JButton browseButton;
  43.236 +    private javax.swing.JLabel certDirectoryLabel;
  43.237 +    private javax.swing.JTextField certTextField;
  43.238 +    private javax.swing.JLabel nameLabel;
  43.239 +    private javax.swing.JTextField nameTextField;
  43.240 +    private javax.swing.JLabel urlLabel;
  43.241 +    private javax.swing.JTextField urlTextField;
  43.242 +    // End of variables declaration//GEN-END:variables
  43.243 +}
    44.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/wizard/DockerConnectionPanel.java	Wed Mar 23 21:02:01 2016 +0000
    44.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/wizard/DockerConnectionPanel.java	Thu Mar 24 15:34:03 2016 +0100
    44.3 @@ -44,24 +44,31 @@
    44.4  import java.io.File;
    44.5  import java.net.MalformedURLException;
    44.6  import java.net.URL;
    44.7 +import java.util.HashMap;
    44.8 +import java.util.Map;
    44.9  import java.util.regex.Matcher;
   44.10  import java.util.regex.Pattern;
   44.11  import javax.swing.event.ChangeEvent;
   44.12  import javax.swing.event.ChangeListener;
   44.13 +import org.netbeans.modules.docker.api.DockerAction;
   44.14  import org.netbeans.modules.docker.api.DockerInstance;
   44.15 -import org.netbeans.modules.docker.api.DockerIntegration;
   44.16 +import org.netbeans.modules.docker.api.DockerSupport;
   44.17  import org.openide.WizardDescriptor;
   44.18 +import org.openide.WizardValidationException;
   44.19  import org.openide.util.ChangeSupport;
   44.20  import org.openide.util.HelpCtx;
   44.21  import org.openide.util.NbBundle;
   44.22  import org.openide.util.Utilities;
   44.23  
   44.24 -public class DockerConnectionPanel implements WizardDescriptor.Panel<WizardDescriptor>, ChangeListener {
   44.25 +@NbBundle.Messages("MSG_InaccessibleSocket=Socket is not accessible.")
   44.26 +public class DockerConnectionPanel implements WizardDescriptor.ExtendedAsynchronousValidatingPanel<WizardDescriptor>, ChangeListener {
   44.27  
   44.28      private static final Pattern REMOTE_HOST_PATTERN = Pattern.compile("^(tcp://)[^/:](:\\d+)($|/.*)"); // NOI18N
   44.29  
   44.30      private final ChangeSupport changeSupport = new ChangeSupport(this);
   44.31  
   44.32 +    private final Map<String, Object> values = new HashMap<>();
   44.33 +
   44.34      /**
   44.35       * The visual component that displays this panel. If you need to access the
   44.36       * component from this class, just use getComponent().
   44.37 @@ -94,6 +101,7 @@
   44.38      @NbBundle.Messages({
   44.39          "MSG_EmptyDisplayName=Display name must not be empty.",
   44.40          "MSG_AlreadyUsedDisplayName=Display name is already used by another instance.",
   44.41 +        "MSG_EmptySocket=Unix socket must not be empty.",
   44.42          "MSG_EmptyUrl=URL must not be empty.",
   44.43          "MSG_InvalidUrl=URL must be valid http or https URL.",
   44.44          "MSG_NonExistingCertificatePath=The certificates path does not exist.",
   44.45 @@ -108,68 +116,145 @@
   44.46          wizard.putProperty(WizardDescriptor.PROP_INFO_MESSAGE, null);
   44.47          wizard.putProperty(WizardDescriptor.PROP_WARNING_MESSAGE, null);
   44.48  
   44.49 -        String displayName = component.getDisplayName();
   44.50 +        Configuration panel = component.getConfiguration();
   44.51 +        String displayName = panel.getDisplayName();
   44.52          if (displayName == null) {
   44.53              wizard.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, Bundle.MSG_EmptyDisplayName());
   44.54              return false;
   44.55          }
   44.56 -        for (DockerInstance instance : DockerIntegration.getDefault().getInstances()) {
   44.57 +        for (DockerInstance instance : DockerSupport.getDefault().getInstances()) {
   44.58              if (displayName.equals(instance.getDisplayName())) {
   44.59                  wizard.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, Bundle.MSG_AlreadyUsedDisplayName());
   44.60                  return false;
   44.61              }
   44.62          }
   44.63  
   44.64 -        String url = component.getUrl();
   44.65 -        if (url == null) {
   44.66 -            wizard.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, Bundle.MSG_EmptyUrl());
   44.67 -            return false;
   44.68 -        }
   44.69 +        if (panel.isSocketSelected()) {
   44.70 +            File socket = panel.getSocket();
   44.71 +            if (socket == null) {
   44.72 +                wizard.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, Bundle.MSG_EmptySocket());
   44.73 +                return false;
   44.74 +            }
   44.75 +            if (!socket.exists() || !socket.canRead() || !socket.canWrite()) {
   44.76 +                wizard.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, Bundle.MSG_InaccessibleSocket());
   44.77 +                return false;
   44.78 +            }
   44.79 +        } else {
   44.80 +            String url = panel.getUrl();
   44.81 +            if (url == null) {
   44.82 +                wizard.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, Bundle.MSG_EmptyUrl());
   44.83 +                return false;
   44.84 +            }
   44.85  
   44.86 -        URL realUrl = null;
   44.87 -        boolean urlWrong = false;
   44.88 -        try {
   44.89 -            realUrl = new URL(url);
   44.90 -            if (!"http".equals(realUrl.getProtocol()) && !"https".equals(realUrl.getProtocol())) { // NOI18N
   44.91 +            URL realUrl = null;
   44.92 +            boolean urlWrong = false;
   44.93 +            try {
   44.94 +                realUrl = new URL(url);
   44.95 +                if (!"http".equals(realUrl.getProtocol()) // NOI18N
   44.96 +                        && !"https".equals(realUrl.getProtocol())) { // NOI18N
   44.97 +                    urlWrong = true;
   44.98 +                }
   44.99 +            } catch (MalformedURLException ex) {
  44.100                  urlWrong = true;
  44.101              }
  44.102 -        } catch (MalformedURLException ex) {
  44.103 -            urlWrong = true;
  44.104 -        }
  44.105 -        if (urlWrong) {
  44.106 -            wizard.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, Bundle.MSG_InvalidUrl());
  44.107 -            return false;
  44.108 -        }
  44.109 -
  44.110 -        String certPath = component.getCertPath();
  44.111 -        if (certPath != null) {
  44.112 -            File certPathFile = new File(certPath);
  44.113 -            if (!certPathFile.isDirectory()) {
  44.114 -                wizard.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, Bundle.MSG_NonExistingCertificatePath());
  44.115 +            if (urlWrong) {
  44.116 +                wizard.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, Bundle.MSG_InvalidUrl());
  44.117                  return false;
  44.118              }
  44.119 -            if (!new File(certPathFile, AddDockerInstanceWizard.DEFAULT_CA_FILE).isFile()) {
  44.120 -                wizard.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE,
  44.121 -                        Bundle.MSG_CertificatePathMissingFile(AddDockerInstanceWizard.DEFAULT_CA_FILE));
  44.122 -                return false;
  44.123 +
  44.124 +            String certPath = panel.getCertPath();
  44.125 +            if (certPath != null) {
  44.126 +                File certPathFile = new File(certPath);
  44.127 +                if (!certPathFile.isDirectory()) {
  44.128 +                    wizard.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE, Bundle.MSG_NonExistingCertificatePath());
  44.129 +                    return false;
  44.130 +                }
  44.131 +                if (!new File(certPathFile, AddDockerInstanceWizard.DEFAULT_CA_FILE).isFile()) {
  44.132 +                    wizard.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE,
  44.133 +                            Bundle.MSG_CertificatePathMissingFile(AddDockerInstanceWizard.DEFAULT_CA_FILE));
  44.134 +                    return false;
  44.135 +                }
  44.136 +                if (!new File(certPathFile, AddDockerInstanceWizard.DEFAULT_CERT_FILE).isFile()) {
  44.137 +                    wizard.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE,
  44.138 +                            Bundle.MSG_CertificatePathMissingFile(AddDockerInstanceWizard.DEFAULT_CERT_FILE));
  44.139 +                    return false;
  44.140 +                }
  44.141 +                if (!new File(certPathFile, AddDockerInstanceWizard.DEFAULT_KEY_FILE).isFile()) {
  44.142 +                    wizard.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE,
  44.143 +                            Bundle.MSG_CertificatePathMissingFile(AddDockerInstanceWizard.DEFAULT_KEY_FILE));
  44.144 +                    return false;
  44.145 +                }
  44.146              }
  44.147 -            if (!new File(certPathFile, AddDockerInstanceWizard.DEFAULT_CERT_FILE).isFile()) {
  44.148 -                wizard.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE,
  44.149 -                        Bundle.MSG_CertificatePathMissingFile(AddDockerInstanceWizard.DEFAULT_CERT_FILE));
  44.150 -                return false;
  44.151 -            }
  44.152 -            if (!new File(certPathFile, AddDockerInstanceWizard.DEFAULT_KEY_FILE).isFile()) {
  44.153 -                wizard.putProperty(WizardDescriptor.PROP_ERROR_MESSAGE,
  44.154 -                        Bundle.MSG_CertificatePathMissingFile(AddDockerInstanceWizard.DEFAULT_KEY_FILE));
  44.155 -                return false;
  44.156 +
  44.157 +            if (realUrl != null && "https".equals(realUrl.getProtocol()) && certPath == null) { // NOI18N
  44.158 +                wizard.putProperty(WizardDescriptor.PROP_WARNING_MESSAGE, Bundle.MSG_NoCertificatesForSecure());
  44.159              }
  44.160          }
  44.161  
  44.162 -        if (realUrl != null && "https".equals(realUrl.getProtocol()) && certPath == null) { // NOI18N
  44.163 -            wizard.putProperty(WizardDescriptor.PROP_WARNING_MESSAGE, Bundle.MSG_NoCertificatesForSecure());
  44.164 +        return true;
  44.165 +    }
  44.166 +
  44.167 +    @Override
  44.168 +    public void prepareValidation() {
  44.169 +        synchronized (values) {
  44.170 +            values.clear();
  44.171 +            Configuration panel = component.getConfiguration();
  44.172 +            values.put(AddDockerInstanceWizard.DISPLAY_NAME_PROPERTY, panel.getDisplayName());
  44.173 +            values.put(AddDockerInstanceWizard.SOCKET_SELECTED_PROPERTY, panel.isSocketSelected());
  44.174 +            values.put(AddDockerInstanceWizard.SOCKET_PROPERTY, panel.getSocket());
  44.175 +            values.put(AddDockerInstanceWizard.URL_PROPERTY, panel.getUrl());
  44.176 +            values.put(AddDockerInstanceWizard.CERTIFICATE_PATH_PROPERTY, panel.getCertPath());
  44.177          }
  44.178 +        component.setWaitingState(true);
  44.179 +    }
  44.180  
  44.181 -        return true;
  44.182 +    @Override
  44.183 +    public void finishValidation() {
  44.184 +        component.setWaitingState(false);
  44.185 +    }
  44.186 +
  44.187 +    @NbBundle.Messages("MSG_CannotConnect=Cannot establish connection.")
  44.188 +    @Override
  44.189 +    public void validate() throws WizardValidationException {
  44.190 +        try {
  44.191 +            DockerInstance instance;
  44.192 +            synchronized (values) {
  44.193 +                boolean socketSelected = (Boolean) values.get(AddDockerInstanceWizard.SOCKET_SELECTED_PROPERTY);
  44.194 +                if (socketSelected) {
  44.195 +                    File socket = (File) values.get(AddDockerInstanceWizard.SOCKET_PROPERTY);
  44.196 +                    // this is repeated here as the acessibility might have change since the last check
  44.197 +                    if (!socket.exists() || !socket.canRead() || !socket.canWrite()) {
  44.198 +                        String error = Bundle.MSG_InaccessibleSocket();
  44.199 +                        throw new WizardValidationException(component, error, error);
  44.200 +                    }
  44.201 +                    instance = DockerInstance.getInstance(Utilities.toURI(socket).toURL().toString(),
  44.202 +                            null, null, null, null);
  44.203 +                } else {
  44.204 +                    File caFile = null;
  44.205 +                    File certFile = null;
  44.206 +                    File keyFile = null;
  44.207 +
  44.208 +                    String strCertPath = (String) values.get(AddDockerInstanceWizard.CERTIFICATE_PATH_PROPERTY);
  44.209 +                    if (strCertPath != null) {
  44.210 +                        File file = new File(strCertPath);
  44.211 +                        caFile = new File(file, AddDockerInstanceWizard.DEFAULT_CA_FILE);
  44.212 +                        certFile = new File(file, AddDockerInstanceWizard.DEFAULT_CERT_FILE);
  44.213 +                        keyFile = new File(file, AddDockerInstanceWizard.DEFAULT_KEY_FILE);
  44.214 +                    }
  44.215 +                    instance = DockerInstance.getInstance((String) values.get(AddDockerInstanceWizard.URL_PROPERTY),
  44.216 +                            null, caFile, certFile, keyFile);
  44.217 +                }
  44.218 +            }
  44.219 +
  44.220 +            DockerAction action = new DockerAction(instance);
  44.221 +            if (!action.ping()) {
  44.222 +                String error = Bundle.MSG_CannotConnect();
  44.223 +                throw new WizardValidationException(component, error, error);
  44.224 +            }
  44.225 +        } catch (MalformedURLException ex) {
  44.226 +            String error = Bundle.MSG_CannotConnect();
  44.227 +            throw new WizardValidationException(component, error, error);
  44.228 +        }
  44.229      }
  44.230  
  44.231      @Override
  44.232 @@ -187,27 +272,42 @@
  44.233      })
  44.234      @Override
  44.235      public void readSettings(WizardDescriptor wiz) {
  44.236 +        boolean init = false;
  44.237          if (wizard == null) {
  44.238 +            init = true;
  44.239              wizard = wiz;
  44.240          }
  44.241  
  44.242 +        Configuration panel = component.getConfiguration();
  44.243          String displayName = (String) wiz.getProperty(AddDockerInstanceWizard.DISPLAY_NAME_PROPERTY);
  44.244 -        if (displayName == null) {
  44.245 +        if (displayName == null && init) {
  44.246              displayName = Bundle.LBL_DefaultDisplayName();
  44.247          }
  44.248 -        component.setDisplayName(displayName);
  44.249 +        panel.setDisplayName(displayName);
  44.250 +
  44.251 +        Boolean socketSelected = (Boolean) wiz.getProperty(AddDockerInstanceWizard.SOCKET_SELECTED_PROPERTY);
  44.252 +        if (socketSelected == null) {
  44.253 +            socketSelected = DockerSupport.getDefault().isSocketSupported();
  44.254 +        }
  44.255 +        panel.setSocketSelected(socketSelected);
  44.256 +
  44.257 +        File socket = (File) wiz.getProperty(AddDockerInstanceWizard.SOCKET_PROPERTY);
  44.258 +        if (socket == null && init && socketSelected) {
  44.259 +            socket = getDefaultSocket();
  44.260 +        }
  44.261 +        panel.setSocket(socket);
  44.262  
  44.263          String url = (String) wiz.getProperty(AddDockerInstanceWizard.URL_PROPERTY);
  44.264 -        if (url == null) {
  44.265 +        if (url == null && init && !socketSelected) {
  44.266              url = getDefaultUrl();
  44.267          }
  44.268 -        component.setUrl(url);
  44.269 +        panel.setUrl(url);
  44.270  
  44.271          String certPath = (String) wiz.getProperty(AddDockerInstanceWizard.CERTIFICATE_PATH_PROPERTY);
  44.272 -        if (certPath == null) {
  44.273 +        if (certPath == null && init && !socketSelected) {
  44.274              certPath = getDefaultCertificatePath();
  44.275          }
  44.276 -        component.setCertPath(certPath);
  44.277 +        panel.setCertPath(certPath);
  44.278  
  44.279          // XXX revalidate; is this bug?
  44.280          changeSupport.fireChange();
  44.281 @@ -215,9 +315,12 @@
  44.282  
  44.283      @Override
  44.284      public void storeSettings(WizardDescriptor wiz) {
  44.285 -        wiz.putProperty(AddDockerInstanceWizard.DISPLAY_NAME_PROPERTY, component.getDisplayName());
  44.286 -        wiz.putProperty(AddDockerInstanceWizard.URL_PROPERTY, component.getUrl());
  44.287 -        wiz.putProperty(AddDockerInstanceWizard.CERTIFICATE_PATH_PROPERTY, component.getCertPath());
  44.288 +        Configuration panel = component.getConfiguration();
  44.289 +        wiz.putProperty(AddDockerInstanceWizard.DISPLAY_NAME_PROPERTY, panel.getDisplayName());
  44.290 +        wiz.putProperty(AddDockerInstanceWizard.SOCKET_SELECTED_PROPERTY, panel.isSocketSelected());
  44.291 +        wiz.putProperty(AddDockerInstanceWizard.SOCKET_PROPERTY, panel.getSocket());
  44.292 +        wiz.putProperty(AddDockerInstanceWizard.URL_PROPERTY, panel.getUrl());
  44.293 +        wiz.putProperty(AddDockerInstanceWizard.CERTIFICATE_PATH_PROPERTY, panel.getCertPath());
  44.294      }
  44.295  
  44.296      @Override
  44.297 @@ -225,6 +328,14 @@
  44.298          changeSupport.fireChange();
  44.299      }
  44.300  
  44.301 +    private static File getDefaultSocket() {
  44.302 +        File file = new File("/var/run/docker.sock"); // NOI18N
  44.303 +        if (file.exists()) {
  44.304 +            return file;
  44.305 +        }
  44.306 +        return null;
  44.307 +    }
  44.308 +
  44.309      private static String getDefaultUrl() {
  44.310          String url = null;
  44.311          String envUrl = System.getenv("DOCKER_HOST"); // NOI18N
    45.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/wizard/DockerConnectionVisual.form	Wed Mar 23 21:02:01 2016 +0000
    45.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/wizard/DockerConnectionVisual.form	Thu Mar 24 15:34:03 2016 +0100
    45.3 @@ -16,100 +16,51 @@
    45.4    <Layout>
    45.5      <DimensionLayout dim="0">
    45.6        <Group type="103" groupAlignment="0" attributes="0">
    45.7 -          <Component id="explanationLabel" pref="0" max="32767" attributes="0"/>
    45.8 -          <Group type="102" alignment="0" attributes="0">
    45.9 -              <Group type="103" groupAlignment="0" attributes="0">
   45.10 -                  <Component id="certDirectoryLabel" alignment="0" min="-2" max="-2" attributes="0"/>
   45.11 -                  <Component id="urlLabel" alignment="0" min="-2" max="-2" attributes="0"/>
   45.12 -                  <Component id="nameLabel" alignment="0" min="-2" max="-2" attributes="0"/>
   45.13 -              </Group>
   45.14 -              <EmptySpace max="-2" attributes="0"/>
   45.15 -              <Group type="103" groupAlignment="0" attributes="0">
   45.16 -                  <Component id="nameTextField" max="32767" attributes="0"/>
   45.17 -                  <Component id="urlTextField" pref="310" max="32767" attributes="0"/>
   45.18 -                  <Group type="102" alignment="0" attributes="0">
   45.19 -                      <Component id="certTextField" max="32767" attributes="0"/>
   45.20 -                      <EmptySpace max="-2" attributes="0"/>
   45.21 -                      <Component id="browseButton" min="-2" max="-2" attributes="0"/>
   45.22 -                  </Group>
   45.23 -              </Group>
   45.24 -          </Group>
   45.25 +          <Component id="mainPanel" max="32767" attributes="0"/>
   45.26 +          <Component id="southPanel" alignment="1" max="32767" attributes="0"/>
   45.27        </Group>
   45.28      </DimensionLayout>
   45.29      <DimensionLayout dim="1">
   45.30        <Group type="103" groupAlignment="0" attributes="0">
   45.31            <Group type="102" alignment="0" attributes="0">
   45.32 -              <Group type="103" groupAlignment="3" attributes="0">
   45.33 -                  <Component id="nameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
   45.34 -                  <Component id="nameTextField" alignment="3" min="-2" max="-2" attributes="0"/>
   45.35 -              </Group>
   45.36 -              <EmptySpace max="-2" attributes="0"/>
   45.37 -              <Group type="103" groupAlignment="3" attributes="0">
   45.38 -                  <Component id="urlLabel" alignment="3" min="-2" max="-2" attributes="0"/>
   45.39 -                  <Component id="urlTextField" alignment="3" min="-2" max="-2" attributes="0"/>
   45.40 -              </Group>
   45.41 -              <EmptySpace max="-2" attributes="0"/>
   45.42 -              <Group type="103" groupAlignment="3" attributes="0">
   45.43 -                  <Component id="certDirectoryLabel" alignment="3" min="-2" max="-2" attributes="0"/>
   45.44 -                  <Component id="certTextField" alignment="3" min="-2" max="-2" attributes="0"/>
   45.45 -                  <Component id="browseButton" alignment="3" min="-2" max="-2" attributes="0"/>
   45.46 -              </Group>
   45.47 +              <Component id="mainPanel" max="-2" attributes="0"/>
   45.48                <EmptySpace type="unrelated" max="-2" attributes="0"/>
   45.49 -              <Component id="explanationLabel" min="-2" max="-2" attributes="0"/>
   45.50 +              <Component id="southPanel" max="32767" attributes="0"/>
   45.51            </Group>
   45.52        </Group>
   45.53      </DimensionLayout>
   45.54    </Layout>
   45.55    <SubComponents>
   45.56 -    <Component class="javax.swing.JLabel" name="urlLabel">
   45.57 -      <Properties>
   45.58 -        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
   45.59 -          <ComponentRef name="urlTextField"/>
   45.60 -        </Property>
   45.61 -        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   45.62 -          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="DockerConnectionVisual.urlLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
   45.63 -        </Property>
   45.64 -      </Properties>
   45.65 -    </Component>
   45.66 -    <Component class="javax.swing.JTextField" name="urlTextField">
   45.67 -    </Component>
   45.68 -    <Component class="javax.swing.JLabel" name="explanationLabel">
   45.69 -      <Properties>
   45.70 -        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   45.71 -          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="DockerConnectionVisual.explanationLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
   45.72 -        </Property>
   45.73 -      </Properties>
   45.74 -    </Component>
   45.75 -    <Component class="javax.swing.JLabel" name="nameLabel">
   45.76 -      <Properties>
   45.77 -        <Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
   45.78 -          <ComponentRef name="nameTextField"/>
   45.79 -        </Property>
   45.80 -        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   45.81 -          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="DockerConnectionVisual.nameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
   45.82 -        </Property>
   45.83 -      </Properties>
   45.84 -    </Component>
   45.85 -    <Component class="javax.swing.JTextField" name="nameTextField">
   45.86 -    </Component>
   45.87 -    <Component class="javax.swing.JLabel" name="certDirectoryLabel">
   45.88 -      <Properties>
   45.89 -        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   45.90 -          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="DockerConnectionVisual.certDirectoryLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
   45.91 -        </Property>
   45.92 -      </Properties>
   45.93 -    </Component>
   45.94 -    <Component class="javax.swing.JTextField" name="certTextField">
   45.95 -    </Component>
   45.96 -    <Component class="javax.swing.JButton" name="browseButton">
   45.97 -      <Properties>
   45.98 -        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
   45.99 -          <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="DockerConnectionVisual.browseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  45.100 -        </Property>
  45.101 -      </Properties>
  45.102 -      <Events>
  45.103 -        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseButtonActionPerformed"/>
  45.104 -      </Events>
  45.105 -    </Component>
  45.106 +    <Container class="javax.swing.JPanel" name="mainPanel">
  45.107 +
  45.108 +      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
  45.109 +    </Container>
  45.110 +    <Container class="javax.swing.JPanel" name="southPanel">
  45.111 +
  45.112 +      <Layout>
  45.113 +        <DimensionLayout dim="0">
  45.114 +          <Group type="103" groupAlignment="0" attributes="0">
  45.115 +              <Group type="102" alignment="0" attributes="0">
  45.116 +                  <Component id="testButton" min="-2" max="-2" attributes="0"/>
  45.117 +                  <EmptySpace min="0" pref="291" max="32767" attributes="0"/>
  45.118 +              </Group>
  45.119 +          </Group>
  45.120 +        </DimensionLayout>
  45.121 +        <DimensionLayout dim="1">
  45.122 +          <Group type="103" groupAlignment="0" attributes="0">
  45.123 +              <Component id="testButton" min="-2" max="-2" attributes="0"/>
  45.124 +          </Group>
  45.125 +        </DimensionLayout>
  45.126 +      </Layout>
  45.127 +      <SubComponents>
  45.128 +        <Component class="javax.swing.JButton" name="testButton">
  45.129 +          <Properties>
  45.130 +            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
  45.131 +              <ResourceString bundle="org/netbeans/modules/docker/ui/wizard/Bundle.properties" key="DockerConnectionVisual.testButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
  45.132 +            </Property>
  45.133 +          </Properties>
  45.134 +        </Component>
  45.135 +      </SubComponents>
  45.136 +    </Container>
  45.137    </SubComponents>
  45.138  </Form>
    46.1 --- a/docker.ui/src/org/netbeans/modules/docker/ui/wizard/DockerConnectionVisual.java	Wed Mar 23 21:02:01 2016 +0000
    46.2 +++ b/docker.ui/src/org/netbeans/modules/docker/ui/wizard/DockerConnectionVisual.java	Thu Mar 24 15:34:03 2016 +0100
    46.3 @@ -41,40 +41,43 @@
    46.4   */
    46.5  package org.netbeans.modules.docker.ui.wizard;
    46.6  
    46.7 -import java.io.File;
    46.8 -import javax.swing.JFileChooser;
    46.9 -import javax.swing.SwingUtilities;
   46.10 +import java.awt.Component;
   46.11 +import java.awt.Cursor;
   46.12 +import javax.swing.JPanel;
   46.13 +import javax.swing.event.ChangeEvent;
   46.14  import javax.swing.event.ChangeListener;
   46.15 -import javax.swing.event.DocumentEvent;
   46.16 -import javax.swing.event.DocumentListener;
   46.17 -import org.netbeans.modules.docker.ui.UiUtils;
   46.18 +import org.netbeans.modules.docker.api.DockerSupport;
   46.19  import org.openide.util.ChangeSupport;
   46.20  import org.openide.util.NbBundle;
   46.21 -import org.openide.util.Utilities;
   46.22  
   46.23  /**
   46.24   *
   46.25   * @author Petr Hejl
   46.26   */
   46.27 -public class DockerConnectionVisual extends javax.swing.JPanel {
   46.28 +public class DockerConnectionVisual extends javax.swing.JPanel implements ChangeListener {
   46.29  
   46.30      private final ChangeSupport changeSupport = new ChangeSupport(this);
   46.31  
   46.32 +    private final Configuration configPanel;
   46.33 +
   46.34      /**
   46.35       * Creates new form DockerWizardVisual
   46.36       */
   46.37      public DockerConnectionVisual() {
   46.38          initComponents();
   46.39  
   46.40 -        // do not show the port binding explanation on mac and windows
   46.41 -        if (Utilities.isMac() || Utilities.isWindows()) {
   46.42 -            explanationLabel.setVisible(false);
   46.43 +        if (DockerSupport.getDefault().isSocketSupported()) {
   46.44 +            configPanel = new ConfigurationLinuxPanel();
   46.45 +        } else {
   46.46 +            configPanel = new ConfigurationPanel();
   46.47          }
   46.48  
   46.49 -        DefaultDocumentListener listener = new DefaultDocumentListener();
   46.50 -        nameTextField.getDocument().addDocumentListener(listener);
   46.51 -        urlTextField.getDocument().addDocumentListener(listener);
   46.52 -        certTextField.getDocument().addDocumentListener(listener);
   46.53 +        configPanel.addChangeListener(this);
   46.54 +        mainPanel.add((JPanel) configPanel);
   46.55 +        
   46.56 +        // disable it - it is not implemented
   46.57 +        // do we really want it?
   46.58 +        testButton.setVisible(false);
   46.59      }
   46.60  
   46.61      public void addChangeListener(ChangeListener l) {
   46.62 @@ -85,28 +88,20 @@
   46.63          changeSupport.removeChangeListener(l);
   46.64      }
   46.65  
   46.66 -    public String getDisplayName() {
   46.67 -        return UiUtils.getValue(nameTextField);
   46.68 +    @Override
   46.69 +    public void stateChanged(ChangeEvent e) {
   46.70 +        changeSupport.fireChange();
   46.71      }
   46.72  
   46.73 -    public void setDisplayName(String displayName) {
   46.74 -        nameTextField.setText(displayName);
   46.75 +    public Configuration getConfiguration() {
   46.76 +        return configPanel;
   46.77      }
   46.78  
   46.79 -    public String getUrl() {
   46.80 -        return UiUtils.getValue(urlTextField);
   46.81 -    }
   46.82 -
   46.83 -    public void setUrl(String url) {
   46.84 -        urlTextField.setText(url);
   46.85 -    }
   46.86 -
   46.87 -    public String getCertPath() {
   46.88 -        return UiUtils.getValue(certTextField);
   46.89 -    }
   46.90 -
   46.91 -    public void setCertPath(String path) {
   46.92 -        certTextField.setText(path);
   46.93 +    public void setWaitingState(boolean wait) {
   46.94 +        Component rootPane = getRootPane();
   46.95 +        if (rootPane != null) {
   46.96 +            rootPane.setCursor(wait ? Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR) : null);
   46.97 +        }
   46.98      }
   46.99  
  46.100      @NbBundle.Messages("MSG_Connection=Connection")
  46.101 @@ -115,24 +110,6 @@
  46.102          return Bundle.MSG_Connection();
  46.103      }
  46.104  
  46.105 -    private class DefaultDocumentListener implements DocumentListener {
  46.106 -
  46.107 -        @Override
  46.108 -        public void insertUpdate(DocumentEvent e) {
  46.109 -            changeSupport.fireChange();
  46.110 -        }
  46.111 -
  46.112 -        @Override
  46.113 -        public void removeUpdate(DocumentEvent e) {
  46.114 -            changeSupport.fireChange();
  46.115 -        }
  46.116 -
  46.117 -        @Override
  46.118 -        public void changedUpdate(DocumentEvent e) {
  46.119 -            changeSupport.fireChange();
  46.120 -        }
  46.121 -    }
  46.122 -
  46.123      /**
  46.124       * This method is called from within the constructor to initialize the form.
  46.125       * WARNING: Do NOT modify this code. The content of this method is always
  46.126 @@ -142,93 +119,47 @@
  46.127      // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  46.128      private void initComponents() {
  46.129  
  46.130 -        urlLabel = new javax.swing.JLabel();
  46.131 -        urlTextField = new javax.swing.JTextField();
  46.132 -        explanationLabel = new javax.swing.JLabel();
  46.133 -        nameLabel = new javax.swing.JLabel();
  46.134 -        nameTextField = new javax.swing.JTextField();
  46.135 -        certDirectoryLabel = new javax.swing.JLabel();
  46.136 -        certTextField = new javax.swing.JTextField();
  46.137 -        browseButton = new javax.swing.JButton();
  46.138 +        mainPanel = new javax.swing.JPanel();
  46.139 +        southPanel = new javax.swing.JPanel();
  46.140 +        testButton = new javax.swing.JButton();
  46.141  
  46.142 -        urlLabel.setLabelFor(urlTextField);
  46.143 -        org.openide.awt.Mnemonics.setLocalizedText(urlLabel, org.openide.util.NbBundle.getMessage(DockerConnectionVisual.class, "DockerConnectionVisual.urlLabel.text")); // NOI18N
  46.144 +        mainPanel.setLayout(new java.awt.BorderLayout());
  46.145  
  46.146 -        org.openide.awt.Mnemonics.setLocalizedText(explanationLabel, org.openide.util.NbBundle.getMessage(DockerConnectionVisual.class, "DockerConnectionVisual.explanationLabel.text")); // NOI18N
  46.147 +        org.openide.awt.Mnemonics.setLocalizedText(testButton, org.openide.util.NbBundle.getMessage(DockerConnectionVisual.class, "DockerConnectionVisual.testButton.text")); // NOI18N
  46.148  
  46.149 -        nameLabel.setLabelFor(nameTextField);
  46.150 -        org.openide.awt.Mnemonics.setLocalizedText(nameLabel, org.openide.util.NbBundle.getMessage(DockerConnectionVisual.class, "DockerConnectionVisual.nameLabel.text")); // NOI18N
  46.151 -
  46.152 -        org.openide.awt.Mnemonics.setLocalizedText(certDirectoryLabel, org.openide.util.NbBundle.getMessage(DockerConnectionVisual.class, "DockerConnectionVisual.certDirectoryLabel.text")); // NOI18N
  46.153 -
  46.154 -        org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(DockerConnectionVisual.class, "DockerConnectionVisual.browseButton.text")); // NOI18N
  46.155 -        browseButton.addActionListener(new java.awt.event.ActionListener() {
  46.156 -            public void actionPerformed(java.awt.event.ActionEvent evt) {
  46.157 -                browseButtonActionPerformed(evt);
  46.158 -            }
  46.159 -        });
  46.160 +        javax.swing.GroupLayout southPanelLayout = new javax.swing.GroupLayout(southPanel);
  46.161 +        southPanel.setLayout(southPanelLayout);
  46.162 +        southPanelLayout.setHorizontalGroup(
  46.163 +            southPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  46.164 +            .addGroup(southPanelLayout.createSequentialGroup()
  46.165 +                .addComponent(testButton)
  46.166 +                .addGap(0, 291, Short.MAX_VALUE))
  46.167 +        );
  46.168 +        southPanelLayout.setVerticalGroup(
  46.169 +            southPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  46.170 +            .addComponent(testButton)
  46.171 +        );
  46.172  
  46.173          javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
  46.174          this.setLayout(layout);
  46.175          layout.setHorizontalGroup(
  46.176              layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  46.177 -            .addComponent(explanationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
  46.178 -            .addGroup(layout.createSequentialGroup()
  46.179 -                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  46.180 -                    .addComponent(certDirectoryLabel)
  46.181 -                    .addComponent(urlLabel)
  46.182 -                    .addComponent(nameLabel))
  46.183 -                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  46.184 -                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  46.185 -                    .addComponent(nameTextField)
  46.186 -                    .addComponent(urlTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 310, Short.MAX_VALUE)
  46.187 -                    .addGroup(layout.createSequentialGroup()
  46.188 -                        .addComponent(certTextField)
  46.189 -                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  46.190 -                        .addComponent(browseButton))))
  46.191 +            .addComponent(mainPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
  46.192 +            .addComponent(southPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
  46.193          );
  46.194          layout.setVerticalGroup(
  46.195              layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  46.196              .addGroup(layout.createSequentialGroup()
  46.197 -                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
  46.198 -                    .addComponent(nameLabel)
  46.199 -                    .addComponent(nameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
  46.200 -                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  46.201 -                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
  46.202 -                    .addComponent(urlLabel)
  46.203 -                    .addComponent(urlTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
  46.204 -                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
  46.205 -                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
  46.206 -                    .addComponent(certDirectoryLabel)
  46.207 -                    .addComponent(certTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
  46.208 -                    .addComponent(browseButton))
  46.209 +                .addComponent(mainPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
  46.210                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
  46.211 -                .addComponent(explanationLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
  46.212 +                .addComponent(southPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
  46.213          );
  46.214      }// </editor-fold>//GEN-END:initComponents
  46.215  
  46.216 -    private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
  46.217 -        JFileChooser chooser = new JFileChooser();
  46.218 -        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
  46.219 -        chooser.setFileHidingEnabled(false);
  46.220 -        String text = certTextField.getText();
  46.221 -        if (text != null && !text.trim().isEmpty()) {
  46.222 -            chooser.setSelectedFile(new File(text));
  46.223 -        }
  46.224 -        if (chooser.showOpenDialog(SwingUtilities.getWindowAncestor(this)) == JFileChooser.APPROVE_OPTION) {
  46.225 -            certTextField.setText(chooser.getSelectedFile().getAbsolutePath());
  46.226 -        }
  46.227 -    }//GEN-LAST:event_browseButtonActionPerformed
  46.228 -
  46.229  
  46.230      // Variables declaration - do not modify//GEN-BEGIN:variables
  46.231 -    private javax.swing.JButton browseButton;
  46.232 -    private javax.swing.JLabel certDirectoryLabel;
  46.233 -    private javax.swing.JTextField certTextField;
  46.234 -    private javax.swing.JLabel explanationLabel;
  46.235 -    private javax.swing.JLabel nameLabel;
  46.236 -    private javax.swing.JTextField nameTextField;
  46.237 -    private javax.swing.JLabel urlLabel;
  46.238 -    private javax.swing.JTextField urlTextField;
  46.239 +    private javax.swing.JPanel mainPanel;
  46.240 +    private javax.swing.JPanel southPanel;
  46.241 +    private javax.swing.JButton testButton;
  46.242      // End of variables declaration//GEN-END:variables
  46.243  }