test/jdk/java/net/SocketImpl/BadClient.java
branchniosocketimpl-branch
changeset 57311 6d80c72b7484
parent 57310 c1fad761a86e
child 57312 36c96936c5bc
equal deleted inserted replaced
57310:c1fad761a86e 57311:6d80c72b7484
     1 /*
       
     2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 /*
       
    25  * @test
       
    26  * @modules java.base/java.net:+open java.base/sun.nio.ch:+open
       
    27  * @run testng/othervm BadClient
       
    28  * @summary Test the SocketImpl for scenarios that do not arise with Socket or
       
    29  *          ServerSocket
       
    30  */
       
    31 
       
    32 import java.io.IOException;
       
    33 import java.io.InputStream;
       
    34 import java.io.OutputStream;
       
    35 import java.lang.reflect.Constructor;
       
    36 import java.lang.reflect.InvocationTargetException;
       
    37 import java.lang.reflect.Method;
       
    38 import java.net.InetAddress;
       
    39 import java.net.InetSocketAddress;
       
    40 import java.net.ServerSocket;
       
    41 import java.net.SocketAddress;
       
    42 import java.net.SocketException;
       
    43 import java.net.SocketImpl;
       
    44 import java.net.SocketOption;
       
    45 import java.net.StandardSocketOptions;
       
    46 import java.util.Set;
       
    47 
       
    48 import org.testng.annotations.Test;
       
    49 import static org.testng.Assert.*;
       
    50 
       
    51 @Test
       
    52 public class BadClient {
       
    53 
       
    54     /**
       
    55      * Test create when already created
       
    56      */
       
    57     public void testCreate1() throws IOException {
       
    58         try (var impl = new PlatformSocketImpl(false)) {
       
    59             impl.create(true);
       
    60             expectThrows(IOException.class, () -> impl.create(true));
       
    61         }
       
    62     }
       
    63 
       
    64     /**
       
    65      * Test create when closed
       
    66      */
       
    67     public void testCreate2() throws IOException {
       
    68         var impl = new PlatformSocketImpl(false);
       
    69         impl.close();
       
    70         expectThrows(IOException.class, () -> impl.create(true));
       
    71     }
       
    72 
       
    73     /**
       
    74      * Test connect when not created
       
    75      */
       
    76     public void testConnect1() throws IOException {
       
    77         try (var ss = new ServerSocket(0)) {
       
    78             var impl = new PlatformSocketImpl(false);
       
    79             var remote = ss.getLocalSocketAddress();
       
    80             expectThrows(IOException.class, () -> impl.connect(remote, 0));
       
    81         }
       
    82     }
       
    83 
       
    84     /**
       
    85      * Test connect with unsupported address type
       
    86      */
       
    87     public void testConnect2() throws IOException {
       
    88         try (var impl = new PlatformSocketImpl(false)) {
       
    89             impl.create(true);
       
    90             var remote = new SocketAddress() { };
       
    91             expectThrows(IOException.class, () -> impl.connect(remote, 0));
       
    92         }
       
    93     }
       
    94 
       
    95     /**
       
    96      * Test connect with an unresolved address
       
    97      */
       
    98     public void testConnect3() throws IOException {
       
    99         try (var impl = new PlatformSocketImpl(false)) {
       
   100             impl.create(true);
       
   101             var remote = new InetSocketAddress("blah-blah.blah-blah", 80);
       
   102             expectThrows(IOException.class, () -> impl.connect(remote, 0));
       
   103         }
       
   104     }
       
   105 
       
   106     /**
       
   107      * Test connect when already connected
       
   108      */
       
   109     public void testConnect4() throws IOException {
       
   110         try (var ss = new ServerSocket(0);
       
   111              var impl = new PlatformSocketImpl(false)) {
       
   112             impl.create(true);
       
   113             impl.connect(ss.getLocalSocketAddress(), 0);
       
   114             var remote = ss.getLocalSocketAddress();
       
   115             expectThrows(IOException.class, () -> impl.connect(remote, 0));
       
   116         }
       
   117     }
       
   118 
       
   119     /**
       
   120      * Test connect when impl is closed
       
   121      */
       
   122     public void testConnect5() throws IOException {
       
   123         try (var ss = new ServerSocket(0)) {
       
   124             var impl = new PlatformSocketImpl(false);
       
   125             impl.close();
       
   126             var remote = ss.getLocalSocketAddress();
       
   127             expectThrows(IOException.class, () -> impl.connect(remote, 0));
       
   128         }
       
   129     }
       
   130 
       
   131     /**
       
   132      * Test connect(String host, int port)
       
   133      */
       
   134     public void testConnect6() throws IOException {
       
   135         try (var ss = new ServerSocket(0);
       
   136              var impl = new PlatformSocketImpl(false)) {
       
   137             impl.create(true);
       
   138             impl.connect(ss.getInetAddress().getHostAddress(), ss.getLocalPort());
       
   139         }
       
   140     }
       
   141 
       
   142     /**
       
   143      * Test connect(InetAddress address, int port)
       
   144      */
       
   145     public void testConnect7() throws IOException {
       
   146         try (var ss = new ServerSocket(0);
       
   147              var impl = new PlatformSocketImpl(false)) {
       
   148             impl.create(true);
       
   149             impl.connect(ss.getInetAddress(), ss.getLocalPort());
       
   150         }
       
   151     }
       
   152 
       
   153     /**
       
   154      * Test bind when not created
       
   155      */
       
   156     public void testBind1() throws IOException {
       
   157         var impl = new PlatformSocketImpl(false);
       
   158         var loopback = InetAddress.getLoopbackAddress();
       
   159         expectThrows(IOException.class, () -> impl.bind(loopback, 0));
       
   160     }
       
   161 
       
   162     /**
       
   163      * Test bind when already bound
       
   164      */
       
   165     public void testBind2() throws IOException {
       
   166         try (var impl = new PlatformSocketImpl(false)) {
       
   167             impl.create(true);
       
   168             var loopback = InetAddress.getLoopbackAddress();
       
   169             impl.bind(loopback, 0);
       
   170             expectThrows(IOException.class, () -> impl.bind(loopback, 0));
       
   171         }
       
   172     }
       
   173 
       
   174     /**
       
   175      * Test bind when connected
       
   176      */
       
   177     public void testBind3() throws IOException {
       
   178         try (var ss = new ServerSocket(0);
       
   179              var impl = new PlatformSocketImpl(false)) {
       
   180             impl.create(true);
       
   181             impl.connect(ss.getLocalSocketAddress(), 0);
       
   182             var loopback = InetAddress.getLoopbackAddress();
       
   183             expectThrows(IOException.class, () -> impl.bind(loopback, 0));
       
   184         }
       
   185     }
       
   186 
       
   187     /**
       
   188      * Test bind when closed
       
   189      */
       
   190     public void testBind4() throws IOException {
       
   191         var impl = new PlatformSocketImpl(false);
       
   192         impl.close();
       
   193         var loopback = InetAddress.getLoopbackAddress();
       
   194         expectThrows(IOException.class, () -> impl.bind(loopback, 0));
       
   195     }
       
   196 
       
   197 
       
   198     /**
       
   199      * Test listen when not created
       
   200      */
       
   201     public void testListen1() {
       
   202         var impl = new PlatformSocketImpl(false);
       
   203         expectThrows(IOException.class, () -> impl.listen(16));
       
   204     }
       
   205 
       
   206     /**
       
   207      * Test listen when not bound
       
   208      */
       
   209     public void testListen2() throws IOException {
       
   210         try (var impl = new PlatformSocketImpl(false)) {
       
   211             impl.create(true);
       
   212             expectThrows(IOException.class, () -> impl.listen(16));
       
   213         }
       
   214     }
       
   215 
       
   216     /**
       
   217      * Test listen when closed
       
   218      */
       
   219     public void testListen3() throws IOException {
       
   220         var impl = new PlatformSocketImpl(false);
       
   221         impl.close();
       
   222         expectThrows(IOException.class, () -> impl.listen(16));
       
   223     }
       
   224 
       
   225     /**
       
   226      * Test accept when not created
       
   227      */
       
   228     public void testAccept1() throws IOException {
       
   229         var impl = new PlatformSocketImpl(true);
       
   230         var si = new PlatformSocketImpl(false);
       
   231         expectThrows(IOException.class, () -> impl.accept(si));
       
   232     }
       
   233 
       
   234     /**
       
   235      * Test accept when not bound
       
   236      */
       
   237     public void testAccept2() throws IOException {
       
   238         try (var impl = new PlatformSocketImpl(true)) {
       
   239             impl.create(true);
       
   240             var si = new PlatformSocketImpl(false);
       
   241             expectThrows(IOException.class, () -> impl.accept(si));
       
   242         }
       
   243     }
       
   244 
       
   245     /**
       
   246      * Test accept when not a stream socket
       
   247      */
       
   248     public void testAccept3() throws IOException {
       
   249         try (var impl = new PlatformSocketImpl(false)) {
       
   250             impl.create(false);
       
   251             impl.bind(InetAddress.getLoopbackAddress(), 0);
       
   252             var si = new PlatformSocketImpl(false);
       
   253             expectThrows(IOException.class, () -> impl.accept(si));
       
   254         }
       
   255     }
       
   256 
       
   257     /**
       
   258      * Test accept when closed
       
   259      */
       
   260     public void testAccept4() throws IOException {
       
   261         var impl = new PlatformSocketImpl(true);
       
   262         impl.close();
       
   263         var si = new PlatformSocketImpl(false);
       
   264         expectThrows(IOException.class, () -> impl.accept(si));
       
   265     }
       
   266 
       
   267     /**
       
   268      * Test accept with SocketImpl that is already created
       
   269      */
       
   270     public void testAccept5() throws IOException {
       
   271         try (var impl = new PlatformSocketImpl(true);
       
   272              var si = new PlatformSocketImpl(false)) {
       
   273             impl.create(true);
       
   274             impl.bind(InetAddress.getLoopbackAddress(), 0);
       
   275             si.create(true);
       
   276             expectThrows(IOException.class, () -> impl.accept(si));
       
   277         }
       
   278     }
       
   279 
       
   280     /**
       
   281      * Test accept with SocketImpl that is closed
       
   282      */
       
   283     public void testAccept6() throws IOException {
       
   284         try (var impl = new PlatformSocketImpl(true);
       
   285              var si = new PlatformSocketImpl(false)) {
       
   286             impl.create(true);
       
   287             impl.bind(InetAddress.getLoopbackAddress(), 0);
       
   288             si.create(true);
       
   289             si.close();
       
   290             expectThrows(IOException.class, () -> impl.accept(si));
       
   291         }
       
   292     }
       
   293 
       
   294     /**
       
   295      * Test available when not created
       
   296      */
       
   297     public void testAvailable1() throws IOException {
       
   298         var impl = new PlatformSocketImpl(false);
       
   299         expectThrows(IOException.class, () -> impl.available());
       
   300     }
       
   301 
       
   302     /**
       
   303      * Test available when created but not connected
       
   304      */
       
   305     public void testAvailable2() throws IOException {
       
   306         try (var impl = new PlatformSocketImpl(false)) {
       
   307             impl.create(true);
       
   308             expectThrows(IOException.class, () -> impl.available());
       
   309         }
       
   310     }
       
   311 
       
   312     /**
       
   313      * Test available when closed
       
   314      */
       
   315     public void testAvailable3() throws IOException {
       
   316         var impl = new PlatformSocketImpl(false);
       
   317         impl.close();
       
   318         expectThrows(IOException.class, () -> impl.available());
       
   319     }
       
   320 
       
   321 
       
   322     /**
       
   323      * Test setOption with unsupported option
       
   324      */
       
   325     public void testSetOption1() throws IOException {
       
   326         try (var impl = new PlatformSocketImpl(false)) {
       
   327             impl.create(true);
       
   328             var opt = new SocketOption<String>() {
       
   329                 @Override public String name() { return "birthday"; }
       
   330                 @Override public Class<String> type() { return String.class; }
       
   331             };
       
   332             expectThrows(UnsupportedOperationException.class, () -> impl.setOption(opt, "today"));
       
   333         }
       
   334     }
       
   335 
       
   336     /**
       
   337      * Test setOption when not created
       
   338      */
       
   339     public void testSetOption2() throws IOException {
       
   340         var impl = new PlatformSocketImpl(false);
       
   341         var opt = StandardSocketOptions.SO_REUSEADDR;
       
   342         expectThrows(IOException.class, () -> impl.setOption(opt, true));
       
   343     }
       
   344 
       
   345     /**
       
   346      * Test setOption when closed
       
   347      */
       
   348     public void testSetOption3() throws IOException {
       
   349         var impl = new PlatformSocketImpl(false);
       
   350         impl.close();
       
   351         var opt = StandardSocketOptions.SO_REUSEADDR;
       
   352         expectThrows(IOException.class, () -> impl.setOption(opt, true));
       
   353     }
       
   354 
       
   355     /**
       
   356      * Test getOption with unsupported option
       
   357      */
       
   358     public void testGetOption1() throws IOException {
       
   359         try (var impl = new PlatformSocketImpl(false)) {
       
   360             impl.create(true);
       
   361             var opt = new SocketOption<String>() {
       
   362                 @Override public String name() { return "birthday"; }
       
   363                 @Override public Class<String> type() { return String.class; }
       
   364             };
       
   365             expectThrows(UnsupportedOperationException.class, () -> impl.getOption(opt));
       
   366         }
       
   367     }
       
   368 
       
   369     /**
       
   370      * Test getOption when not created
       
   371      */
       
   372     public void testGetOption2() throws IOException {
       
   373         var impl = new PlatformSocketImpl(false);
       
   374         var opt = StandardSocketOptions.SO_REUSEADDR;
       
   375         expectThrows(IOException.class, () -> impl.getOption(opt));
       
   376     }
       
   377 
       
   378     /**
       
   379      * Test getOption when closed
       
   380      */
       
   381     public void testGetOption3() throws IOException {
       
   382         var impl = new PlatformSocketImpl(false);
       
   383         impl.close();
       
   384         var opt = StandardSocketOptions.SO_REUSEADDR;
       
   385         expectThrows(IOException.class, () -> impl.getOption(opt));
       
   386     }
       
   387 
       
   388     /**
       
   389      * Test shutdownInput when not created
       
   390      */
       
   391     public void testShutdownInput1() throws IOException {
       
   392         var impl = new PlatformSocketImpl(false);
       
   393         expectThrows(IOException.class, () -> impl.shutdownInput());
       
   394     }
       
   395 
       
   396     /**
       
   397      * Test shutdownInput when not connected
       
   398      */
       
   399     public void testShutdownInput2() throws IOException {
       
   400         try (var impl = new PlatformSocketImpl(false)) {
       
   401             impl.create(true);
       
   402             expectThrows(IOException.class, () -> impl.shutdownInput());
       
   403         }
       
   404     }
       
   405 
       
   406     /**
       
   407      * Test shutdownInput when closed
       
   408      */
       
   409     public void testShutdownInput3() throws IOException {
       
   410         var impl = new PlatformSocketImpl(false);
       
   411         impl.close();
       
   412         expectThrows(IOException.class, () -> impl.shutdownInput());
       
   413     }
       
   414 
       
   415     /**
       
   416      * Test shutdownOutput when not created
       
   417      */
       
   418     public void testShutdownOutput1() throws IOException {
       
   419         var impl = new PlatformSocketImpl(false);
       
   420         expectThrows(IOException.class, () -> impl.shutdownOutput());
       
   421     }
       
   422 
       
   423     /**
       
   424      * Test shutdownOutput when not connected
       
   425      */
       
   426     public void testShutdownOutput2() throws IOException {
       
   427         try (var impl = new PlatformSocketImpl(false)) {
       
   428             impl.create(true);
       
   429             expectThrows(IOException.class, () -> impl.shutdownOutput());
       
   430         }
       
   431     }
       
   432 
       
   433     /**
       
   434      * Test shutdownOutput when closed
       
   435      */
       
   436     public void testShutdownOutput3() throws IOException {
       
   437         var impl = new PlatformSocketImpl(false);
       
   438         impl.close();
       
   439         expectThrows(IOException.class, () -> impl.shutdownOutput());
       
   440     }
       
   441 
       
   442     /**
       
   443      * Test sendUrgentData when not created
       
   444      */
       
   445     public void testSendUrgentData1() throws IOException {
       
   446         var impl = new PlatformSocketImpl(false);
       
   447         expectThrows(IOException.class, () -> impl.sendUrgentData(0));
       
   448     }
       
   449 
       
   450     /**
       
   451      * Test sendUrgentData when not connected
       
   452      */
       
   453     public void testSendUrgentData2() throws IOException {
       
   454         try (var impl = new PlatformSocketImpl(false)) {
       
   455             impl.create(true);
       
   456             expectThrows(IOException.class, () -> impl.sendUrgentData(0));
       
   457         }
       
   458     }
       
   459 
       
   460     /**
       
   461      * Test sendUrgentData when closed
       
   462      */
       
   463     public void testSendUrgentData3() throws IOException {
       
   464         var impl = new PlatformSocketImpl(false);
       
   465         impl.close();
       
   466         expectThrows(IOException.class, () -> impl.sendUrgentData(0));
       
   467     }
       
   468 
       
   469 
       
   470     /**
       
   471      * A SocketImpl that delegates all operations to a NioSocketImpl.
       
   472      */
       
   473     static class PlatformSocketImpl extends SocketImpl implements AutoCloseable {
       
   474         private final SocketImpl impl;
       
   475 
       
   476         PlatformSocketImpl(boolean server) {
       
   477             try {
       
   478                 Class<?> clazz = Class.forName("sun.nio.ch.NioSocketImpl");
       
   479                 Constructor<?> ctor = clazz.getConstructor(boolean.class);
       
   480                 this.impl = (SocketImpl) ctor.newInstance(server);
       
   481             } catch (Exception e) {
       
   482                 throw new RuntimeException(e);
       
   483             }
       
   484         }
       
   485 
       
   486         @Override
       
   487         public void close() throws IOException {
       
   488             find("close").invoke(impl);
       
   489         }
       
   490 
       
   491         @Override
       
   492         protected void create(boolean stream) throws IOException {
       
   493             find("create", boolean.class).invoke(impl, stream);
       
   494         }
       
   495 
       
   496         @Override
       
   497         protected void connect(SocketAddress remote, int millis) throws IOException {
       
   498             find("connect", SocketAddress.class, int.class).invoke(impl, remote, millis);
       
   499         }
       
   500 
       
   501         @Override
       
   502         protected void connect(String host, int port) throws IOException {
       
   503             find("connect", String.class, int.class).invoke(impl, host, port);
       
   504         }
       
   505 
       
   506         @Override
       
   507         protected void connect(InetAddress address, int port) throws IOException {
       
   508             find("connect", InetAddress.class, int.class).invoke(impl, address, port);
       
   509         }
       
   510 
       
   511         @Override
       
   512         protected void bind(InetAddress address, int port) throws IOException {
       
   513             find("bind", InetAddress.class, int.class).invoke(impl, address, port);
       
   514         }
       
   515 
       
   516         @Override
       
   517         protected void listen(int backlog) throws IOException {
       
   518             find("listen", int.class).invoke(impl, backlog);
       
   519         }
       
   520 
       
   521         @Override
       
   522         protected void accept(SocketImpl si) throws IOException {
       
   523             find("accept", SocketImpl.class).invoke(this.impl, ((PlatformSocketImpl) si).impl);
       
   524         }
       
   525 
       
   526         @Override
       
   527         protected InputStream getInputStream() throws IOException {
       
   528             return (InputStream) find("getInputStream").invoke(impl);
       
   529         }
       
   530 
       
   531         @Override
       
   532         protected OutputStream getOutputStream() throws IOException {
       
   533             return (OutputStream) find("getOutputStream").invoke(impl);
       
   534         }
       
   535 
       
   536         @Override
       
   537         protected int available() throws IOException {
       
   538             return (int) find("available").invoke(impl);
       
   539         }
       
   540 
       
   541         @Override
       
   542         protected Set<SocketOption<?>> supportedOptions() {
       
   543             try {
       
   544                 return (Set<SocketOption<?>>) find("supportedOptions").invoke(impl);
       
   545             } catch (IOException ioe) {
       
   546                 throw new RuntimeException(ioe);
       
   547             }
       
   548         }
       
   549 
       
   550         @Override
       
   551         protected <T> void setOption(SocketOption<T> opt, T value) throws IOException {
       
   552             find("setOption", SocketOption.class, Object.class).invoke(impl, opt, value);
       
   553         }
       
   554 
       
   555         @Override
       
   556         protected <T> T getOption(SocketOption<T> opt) throws IOException {
       
   557             return (T) find("getOption", SocketOption.class).invoke(impl, opt);
       
   558         }
       
   559 
       
   560         @Override
       
   561         public void setOption(int opt, Object value) throws SocketException {
       
   562             try {
       
   563                 find("setOption", int.class, Object.class).invoke(impl, opt, value);
       
   564             } catch (SocketException e) {
       
   565                 throw e;
       
   566             } catch (IOException ioe) {
       
   567                 throw new RuntimeException(ioe);
       
   568             }
       
   569         }
       
   570 
       
   571         @Override
       
   572         public Object getOption(int opt) throws SocketException {
       
   573             try {
       
   574                 return find("getOption", int.class).invoke(impl, opt);
       
   575             } catch (SocketException e) {
       
   576                 throw e;
       
   577             } catch (IOException ioe) {
       
   578                 throw new RuntimeException(ioe);
       
   579             }
       
   580         }
       
   581 
       
   582         @Override
       
   583         protected void shutdownInput() throws IOException {
       
   584             find("shutdownInput").invoke(impl);
       
   585         }
       
   586 
       
   587         @Override
       
   588         protected void shutdownOutput() throws IOException {
       
   589             find("shutdownOutput").invoke(impl);
       
   590         }
       
   591 
       
   592         @Override
       
   593         protected boolean supportsUrgentData() {
       
   594             try {
       
   595                 return (boolean) find("supportsUrgentData").invoke(impl);
       
   596             } catch (IOException ioe) {
       
   597                 throw new RuntimeException(ioe);
       
   598             }
       
   599         }
       
   600 
       
   601         @Override
       
   602         protected void sendUrgentData(int data) throws IOException {
       
   603             find("sendUrgentData", int.class).invoke(impl, data);
       
   604         }
       
   605 
       
   606         private MethodInvoker find(String name, Class<?>... paramTypes) {
       
   607             try {
       
   608                 Method m = SocketImpl.class.getDeclaredMethod(name, paramTypes);
       
   609                 m.setAccessible(true);
       
   610                 return new MethodInvoker(m);
       
   611             } catch (Exception e) {
       
   612                 throw new RuntimeException(e);
       
   613             }
       
   614         }
       
   615 
       
   616         static class MethodInvoker {
       
   617             private final Method method;
       
   618 
       
   619             MethodInvoker(Method method) {
       
   620                 this.method = method;
       
   621             }
       
   622 
       
   623             Object invoke(Object target, Object... args) throws IOException {
       
   624                 try {
       
   625                     return method.invoke(target, args);
       
   626                 } catch (InvocationTargetException e) {
       
   627                     Throwable cause = e.getCause();
       
   628                     if (cause instanceof IOException) {
       
   629                         throw (IOException) cause;
       
   630                     } else if (cause instanceof RuntimeException) {
       
   631                         throw (RuntimeException) cause;
       
   632                     } else if (cause instanceof Error) {
       
   633                         throw (Error) cause;
       
   634                     } else {
       
   635                         throw new RuntimeException(e);
       
   636                     }
       
   637                 } catch (Exception e) {
       
   638                     throw new RuntimeException(e);
       
   639                 }
       
   640             }
       
   641         }
       
   642     }
       
   643 }