コード例 #1
0
    /**
     * @param log Logger.
     * @param job Actual job.
     */
    private void onMasterLeave(GridLogger log, Object job) {
      log.info("Callback executed: " + job);

      latch0.countDown();

      invokeLatch.countDown();
    }
コード例 #2
0
    /** {@inheritDoc} */
    @Override
    public Serializable execute() {
      int arg = this.<Integer>argument(0);

      try {
        if (log.isInfoEnabled()) log.info("Executing job [job=" + this + ", arg=" + arg + ']');

        startSignal.countDown();

        try {
          if (!startSignal.await(WAIT_TIME, TimeUnit.MILLISECONDS)) fail();

          if (arg == 1) {
            if (log.isInfoEnabled()) log.info("Job one is proceeding.");
          } else Thread.sleep(WAIT_TIME);
        } catch (InterruptedException e) {
          if (log.isInfoEnabled())
            log.info("Job got cancelled [arg=" + arg + ", ses=" + ses + ", e=" + e + ']');

          return 0;
        }

        if (log.isInfoEnabled()) log.info("Completing job: " + ses);

        return argument(0);
      } finally {
        stopSignal.countDown();

        processedCnt++;
      }
    }
コード例 #3
0
ファイル: ObservableTests.java プロジェクト: spoon-bot/RxJava
  @Test
  public void testReplay() throws InterruptedException {
    final AtomicInteger counter = new AtomicInteger();
    ConnectableObservable<String> o =
        Observable.<String>create(
                observer -> {
                  observer.onSubscribe(EmptySubscription.INSTANCE);
                  new Thread(
                          new Runnable() {

                            @Override
                            public void run() {
                              counter.incrementAndGet();
                              observer.onNext("one");
                              observer.onComplete();
                            }
                          })
                      .start();
                })
            .replay();

    // we connect immediately and it will emit the value
    Disposable s = o.connect();
    try {

      // we then expect the following 2 subscriptions to get that same value
      final CountDownLatch latch = new CountDownLatch(2);

      // subscribe once
      o.subscribe(
          v -> {
            assertEquals("one", v);
            latch.countDown();
          });

      // subscribe again
      o.subscribe(
          v -> {
            assertEquals("one", v);
            latch.countDown();
          });

      if (!latch.await(1000, TimeUnit.MILLISECONDS)) {
        fail("subscriptions did not receive values");
      }
      assertEquals(1, counter.get());
    } finally {
      s.dispose();
    }
  }
コード例 #4
0
  /**
   * @param expJobs Expected jobs number.
   * @param taskStarter Task started.
   * @throws Exception If failed.
   */
  private void testMasterLeaveAwareCallback(
      int expJobs, GridClosure<GridProjection, GridFuture<?>> taskStarter) throws Exception {
    jobLatch = new CountDownLatch(expJobs);
    invokeLatch = new CountDownLatch(expJobs);

    for (int i = 0; i < GRID_CNT; i++) startGrid(i);

    int lastGridIdx = GRID_CNT - 1;

    GridFuture<?> fut = taskStarter.apply(grid(lastGridIdx).forPredicate(excludeLastPredicate()));

    jobLatch.await();

    stopGrid(lastGridIdx, true);

    latch.countDown();

    assert invokeLatch.await(5000, MILLISECONDS);

    try {
      fut.get();
    } catch (GridException e) {
      log.debug("Task failed: " + e);
    }
  }
コード例 #5
0
  /**
   * Ensure that {@link GridComputeJobMasterLeaveAware} callback is invoked when fails to send
   * {@link GridJobExecuteResponse} to master node.
   *
   * @throws Exception If failed.
   */
  public void testCannotSendJobExecuteResponse() throws Exception {
    awaitMasterLeaveCallback = false;

    // Start grids.
    for (int i = 0; i < GRID_CNT; i++) startGrid(i);

    int lastGridIdx = GRID_CNT - 1;

    grid(lastGridIdx)
        .forPredicate(excludeLastPredicate())
        .compute()
        .execute(new TestTask(GRID_CNT - 1), null);

    jobLatch.await();

    for (int i = 0; i < lastGridIdx; i++)
      ((CommunicationSpi) grid(i).configuration().getCommunicationSpi()).waitLatch();

    latch.countDown();

    // Ensure that all worker nodes has already started job response sending.
    for (int i = 0; i < lastGridIdx; i++)
      ((CommunicationSpi) grid(i).configuration().getCommunicationSpi()).awaitResponse();

    // Now we stop master grid.
    stopGrid(lastGridIdx, true);

    // Release communication SPI wait latches. As master node is stopped, job worker will receive
    // and exception.
    for (int i = 0; i < lastGridIdx; i++)
      ((CommunicationSpi) grid(i).configuration().getCommunicationSpi()).releaseWaitLatch();

    assert invokeLatch.await(5000, MILLISECONDS);
  }
コード例 #6
0
  /**
   * Ensure that {@link GridComputeJobMasterLeaveAware} callback is invoked on job which is
   * initiated by master and is currently running on it.
   *
   * @throws Exception If failed.
   */
  public void testLocalJobOnMaster() throws Exception {
    invokeLatch = new CountDownLatch(1);
    jobLatch = new CountDownLatch(1);

    Grid g = startGrid(0);

    g.compute().execute(new TestTask(1), null);

    jobLatch.await();

    // Count down the latch in a separate thread.
    new Thread(
            new Runnable() {
              @Override
              public void run() {
                try {
                  U.sleep(500);
                } catch (GridInterruptedException ignore) {
                  // No-op.
                }

                latch.countDown();
              }
            })
        .start();

    stopGrid(0, true);

    latch.countDown();

    assert invokeLatch.await(5000, MILLISECONDS);
  }
コード例 #7
0
    /** @param log Logger. */
    private void execute(GridLogger log) {
      try {
        log.info("Started execute.");

        // Countdown shared job latch so that the main thread know that all jobs are
        // inside the "execute" routine.
        jobLatch.countDown();

        log.info("After job latch.");

        // Await for the main thread to allow jobs to proceed.
        latch.await();

        log.info("After latch.");

        if (awaitMasterLeaveCallback) {
          latch0.await();

          log.info("After latch0.");
        } else log.info("Latch 0 skipped.");
      } catch (InterruptedException e) {
        // We do not expect any interruptions here, hence this statement.
        fail("Unexpected exception: " + e);
      }
    }
コード例 #8
0
ファイル: ObservableTests.java プロジェクト: spoon-bot/RxJava
  @Test
  public void testPublishLast() throws InterruptedException {
    final AtomicInteger count = new AtomicInteger();
    ConnectableObservable<String> connectable =
        Observable.<String>create(
                observer -> {
                  observer.onSubscribe(EmptySubscription.INSTANCE);
                  count.incrementAndGet();
                  new Thread(
                          () -> {
                            observer.onNext("first");
                            observer.onNext("last");
                            observer.onComplete();
                          })
                      .start();
                })
            .takeLast(1)
            .publish();

    // subscribe once
    final CountDownLatch latch = new CountDownLatch(1);
    connectable.subscribe(
        value -> {
          assertEquals("last", value);
          latch.countDown();
        });

    // subscribe twice
    connectable.subscribe();

    Disposable subscription = connectable.connect();
    assertTrue(latch.await(1000, TimeUnit.MILLISECONDS));
    assertEquals(1, count.get());
    subscription.dispose();
  }
コード例 #9
0
  /**
   * Event callback.
   *
   * @param exchId Exchange ID.
   * @param discoEvt Discovery event.
   */
  public void onEvent(GridDhtPartitionExchangeId exchId, DiscoveryEvent discoEvt) {
    assert exchId.equals(this.exchId);

    this.discoEvt = discoEvt;

    evtLatch.countDown();
  }
コード例 #10
0
ファイル: DefaultClient.java プロジェクト: jingchan/jh_rogue
  protected void dispatch(Message m) {
    // Pull off the connection management messages we're
    // interested in and then pass on the rest.
    if (m instanceof ClientRegistrationMessage) {
      // Then we've gotten our real id
      this.id = (int) ((ClientRegistrationMessage) m).getId();
      log.log(Level.INFO, "Connection established, id:{0}.", this.id);
      connecting.countDown();
      fireConnected();
      return;
    }
    if (m instanceof DisconnectMessage) {
      // Can't do too much else yet
      String reason = ((DisconnectMessage) m).getReason();
      log.log(Level.SEVERE, "Connection terminated, reason:{0}.", reason);
      DisconnectInfo info = new DisconnectInfo();
      info.reason = reason;
      fireDisconnected(info);
      close();
    }

    // Make sure client MessageListeners are called single-threaded
    // since it could receive messages from the TCP and UDP
    // thread simultaneously.
    synchronized (this) {
      messageListeners.messageReceived(this, m);
    }
  }
コード例 #11
0
ファイル: ObservableTests.java プロジェクト: spoon-bot/RxJava
 /**
  * https://github.com/ReactiveX/RxJava/issues/198
  *
  * <p>Rx Design Guidelines 5.2
  *
  * <p>"when calling the Subscribe method that only has an onNext argument, the OnError behavior
  * will be to rethrow the exception on the thread that the message comes out from the Observable.
  * The OnCompleted behavior in this case is to do nothing."
  *
  * @throws InterruptedException
  */
 @Test
 @Ignore("Subscribers can't throw")
 public void testErrorThrownWithoutErrorHandlerAsynchronous() throws InterruptedException {
   final CountDownLatch latch = new CountDownLatch(1);
   final AtomicReference<Throwable> exception = new AtomicReference<>();
   Observable.create(
           observer -> {
             new Thread(
                     () -> {
                       try {
                         observer.onError(new Error("failure"));
                       } catch (Throwable e) {
                         // without an onError handler it has to just throw on whatever thread
                         // invokes it
                         exception.set(e);
                       }
                       latch.countDown();
                     })
                 .start();
           })
       .subscribe();
   // wait for exception
   latch.await(3000, TimeUnit.MILLISECONDS);
   assertNotNull(exception.get());
   assertEquals("failure", exception.get().getMessage());
 }
コード例 #12
0
  @Test
  public void testPreregisteredExecutionCallbackCompletableFuture() throws Exception {
    HazelcastInstanceProxy proxy = (HazelcastInstanceProxy) createHazelcastInstance();
    Field originalField = HazelcastInstanceProxy.class.getDeclaredField("original");
    originalField.setAccessible(true);
    HazelcastInstanceImpl hz = (HazelcastInstanceImpl) originalField.get(proxy);
    NodeEngine nodeEngine = hz.node.nodeEngine;
    ExecutionService es = nodeEngine.getExecutionService();

    final CountDownLatch latch1 = new CountDownLatch(1);
    final CountDownLatch latch2 = new CountDownLatch(1);
    final ExecutorService executorService = Executors.newSingleThreadExecutor();
    try {
      Future future =
          executorService.submit(
              new Callable<String>() {
                @Override
                public String call() {
                  try {
                    latch1.await(30, TimeUnit.SECONDS);
                    return "success";
                  } catch (Exception e) {
                    throw new RuntimeException(e);
                  }
                }
              });

      final AtomicReference reference = new AtomicReference();
      final ICompletableFuture completableFuture = es.asCompletableFuture(future);
      completableFuture.andThen(
          new ExecutionCallback() {
            @Override
            public void onResponse(Object response) {
              reference.set(response);
              latch2.countDown();
            }

            @Override
            public void onFailure(Throwable t) {
              reference.set(t);
              latch2.countDown();
            }
          });

      latch1.countDown();
      latch2.await(30, TimeUnit.SECONDS);
      assertEquals("success", reference.get());

    } finally {
      executorService.shutdown();
    }
  }
コード例 #13
0
ファイル: ObservableTests.java プロジェクト: spoon-bot/RxJava
  @Test
  public void testCacheWithCapacity() throws InterruptedException {
    final AtomicInteger counter = new AtomicInteger();
    Observable<String> o =
        Observable.<String>create(
                observer -> {
                  observer.onSubscribe(EmptySubscription.INSTANCE);
                  new Thread(
                          () -> {
                            counter.incrementAndGet();
                            observer.onNext("one");
                            observer.onComplete();
                          })
                      .start();
                })
            .cache(1);

    // we then expect the following 2 subscriptions to get that same value
    final CountDownLatch latch = new CountDownLatch(2);

    // subscribe once
    o.subscribe(
        v -> {
          assertEquals("one", v);
          latch.countDown();
        });

    // subscribe again
    o.subscribe(
        v -> {
          assertEquals("one", v);
          latch.countDown();
        });

    if (!latch.await(1000, TimeUnit.MILLISECONDS)) {
      fail("subscriptions did not receive values");
    }
    assertEquals(1, counter.get());
  }
コード例 #14
0
 public DesignCIManifestRfcTouple call() {
   String oldThreadName = Thread.currentThread().getName();
   try {
     Thread.currentThread().setName(getProcessingThreadName(oldThreadName, env.getCiId()));
     ManifestRfcContainer manifestPlatformRfcs =
         manifestRfcProcessor.processPlatform(
             platRelation.getToCi(), env, nsPath, userId, availMode);
     DesignCIManifestRfcTouple touple =
         new DesignCIManifestRfcTouple(platRelation.getToCi().getCiId(), manifestPlatformRfcs);
     return touple;
   } finally {
     countDownLatch.countDown();
     Thread.currentThread().setName(oldThreadName);
   }
 }
コード例 #15
0
  /**
   * Ensure that {@link GridComputeJobMasterLeaveAware} callback is invoked when master node leaves
   * topology normally.
   *
   * @throws Exception If failed.
   */
  public void testMasterStoppedNormally() throws Exception {
    // Start grids.
    for (int i = 0; i < GRID_CNT; i++) startGrid(i);

    int lastGridIdx = GRID_CNT - 1;

    grid(lastGridIdx)
        .forPredicate(excludeLastPredicate())
        .compute()
        .execute(new TestTask(GRID_CNT - 1), null);

    jobLatch.await();

    stopGrid(lastGridIdx, true);

    latch.countDown();

    assert invokeLatch.await(5000, MILLISECONDS);
  }
コード例 #16
0
    /**
     * Send message optionally either blocking it or throwing an exception if it is of {@link
     * GridJobExecuteResponse} type.
     *
     * @param node Destination node.
     * @param msg Message to be sent.
     * @throws GridSpiException If failed.
     */
    private void sendMessage0(GridNode node, GridTcpCommunicationMessageAdapter msg)
        throws GridSpiException {
      if (msg instanceof GridIoMessage) {
        GridIoMessage msg0 = (GridIoMessage) msg;

        if (msg0.message() instanceof GridJobExecuteResponse) {
          respLatch.countDown();

          if (wait) {
            try {
              U.await(waitLatch);
            } catch (GridInterruptedException ignore) {
              // No-op.
            }
          }
        }
      }

      if (!block) super.sendMessage(node, msg);
    }
コード例 #17
0
  /**
   * Ensure that {@link GridComputeJobMasterLeaveAware} callback is invoked when master node leaves
   * topology abruptly (e.g. due to a network failure or immediate node shutdown).
   *
   * @throws Exception If failed.
   */
  public void testMasterStoppedAbruptly() throws Exception {
    // Start grids.
    for (int i = 0; i < GRID_CNT; i++) startGrid(i);

    int lastGridIdx = GRID_CNT - 1;

    grid(lastGridIdx)
        .forPredicate(excludeLastPredicate())
        .compute()
        .execute(new TestTask(GRID_CNT - 1), null);

    jobLatch.await();

    ((CommunicationSpi) grid(lastGridIdx).configuration().getCommunicationSpi()).blockMessages();

    stopGrid(lastGridIdx, true);

    latch.countDown();

    assert invokeLatch.await(5000, MILLISECONDS);
  }
コード例 #18
0
  /** @throws Exception Thrown if test failed. */
  public void testC() throws Exception {
    final Collection<SampleBean> set = new GridConcurrentWeakHashSet<>();

    int threadCnt = 2;

    final int cnt = 5;

    final CountDownLatch start = new CountDownLatch(1);
    final CountDownLatch stop = new CountDownLatch(threadCnt);

    Runnable r =
        new Runnable() {
          @Override
          public void run() {
            try {
              start.await();

              for (int i = 0; i < cnt; i++) {
                for (int j = 0; j < cnt; j++) set.add(new SampleBean(i));
              }
            } catch (Exception e) {
              error(e.getMessage());
            }

            stop.countDown();
          }
        };

    for (int i = 0; i < threadCnt; i++) new Thread(r).start();

    start.countDown();

    stop.await();

    assert set.size() == cnt;

    gc();

    assert set.isEmpty();
  }
コード例 #19
0
ファイル: DefaultClient.java プロジェクト: jingchan/jh_rogue
  public void close() {
    checkRunning();

    // Send a close message

    // Tell the thread it's ok to die
    if (fastAdapter != null) {
      fastAdapter.close();
    }
    if (reliableAdapter != null) {
      reliableAdapter.close();
    }

    // Wait for the threads?

    // Just in case we never fully connected
    connecting.countDown();

    fireDisconnected(null);

    isRunning = false;
  }
コード例 #20
0
  /**
   * Initializes store.
   *
   * @throws GridException If failed to initialize.
   */
  private void init() throws GridException {
    if (initGuard.compareAndSet(false, true)) {
      if (log.isDebugEnabled()) log.debug("Initializing cache store.");

      try {
        if (sesFactory != null)
          // Session factory has been provided - nothing to do.
          return;

        if (!F.isEmpty(hibernateCfgPath)) {
          try {
            URL url = new URL(hibernateCfgPath);

            sesFactory = new Configuration().configure(url).buildSessionFactory();

            if (log.isDebugEnabled()) log.debug("Configured session factory using URL: " + url);

            // Session factory has been successfully initialized.
            return;
          } catch (MalformedURLException e) {
            if (log.isDebugEnabled())
              log.debug("Caught malformed URL exception: " + e.getMessage());
          }

          // Provided path is not a valid URL. File?
          File cfgFile = new File(hibernateCfgPath);

          if (cfgFile.exists()) {
            sesFactory = new Configuration().configure(cfgFile).buildSessionFactory();

            if (log.isDebugEnabled())
              log.debug("Configured session factory using file: " + hibernateCfgPath);

            // Session factory has been successfully initialized.
            return;
          }

          // Provided path is not a file. Classpath resource?
          sesFactory = new Configuration().configure(hibernateCfgPath).buildSessionFactory();

          if (log.isDebugEnabled())
            log.debug("Configured session factory using classpath resource: " + hibernateCfgPath);
        } else {
          if (hibernateProps == null) {
            U.warn(
                log, "No Hibernate configuration has been provided for store (will use default).");

            hibernateProps = new Properties();

            hibernateProps.setProperty("hibernate.connection.url", DFLT_CONN_URL);
            hibernateProps.setProperty("hibernate.show_sql", DFLT_SHOW_SQL);
            hibernateProps.setProperty("hibernate.hbm2ddl.auto", DFLT_HBM2DDL_AUTO);
          }

          Configuration cfg = new Configuration();

          cfg.setProperties(hibernateProps);

          assert resourceAvailable(MAPPING_RESOURCE);

          cfg.addResource(MAPPING_RESOURCE);

          sesFactory = cfg.buildSessionFactory();

          if (log.isDebugEnabled())
            log.debug("Configured session factory using properties: " + hibernateProps);
        }
      } catch (HibernateException e) {
        throw new GridException("Failed to initialize store.", e);
      } finally {
        initLatch.countDown();
      }
    } else if (initLatch.getCount() > 0) U.await(initLatch);

    if (sesFactory == null) throw new GridException("Cache store was not properly initialized.");
  }
コード例 #21
0
  private void processMessage(String message) throws Exception {
    Boolean toStdOut = logAllMessagesForUsers.get(config.getUsername());
    if (toStdOut != null) {
      if (toStdOut) System.out.println("IMAPrcv[" + config.getUsername() + "]: " + message);
      else log.info("IMAPrcv[{}]: {}", config.getUsername(), message);
    }

    wireTrace.add(message);
    log.trace(message);
    if (SYSTEM_ERROR_REGEX.matcher(message).matches()
        || ". NO [ALERT] Account exceeded command or bandwidth limits. (Failure)"
            .equalsIgnoreCase(message.trim())) {
      log.warn(
          "{} disconnected by IMAP Server due to system error: {}", config.getUsername(), message);
      disconnectAbnormally(message);
      return;
    }

    try {
      if (halt) {
        log.error(
            "Mail client for {} is halted but continues to receive messages, ignoring!",
            config.getUsername());
        return;
      }
      if (loginSuccess.getCount() > 0) {
        if (message.startsWith(CAPABILITY_PREFIX)) {
          this.capabilities =
              Arrays.asList(message.substring(CAPABILITY_PREFIX.length() + 1).split("[ ]+"));
          return;
        } else if (AUTH_SUCCESS_REGEX.matcher(message).matches()) {
          log.info("Authentication success for user {}", config.getUsername());
          loginSuccess.countDown();
        } else {
          Matcher matcher = COMMAND_FAILED_REGEX.matcher(message);
          if (matcher.find()) {
            // WARNING: DO NOT COUNTDOWN THE LOGIN LATCH ON FAILURE!!!

            log.warn("Authentication failed for {} due to: {}", config.getUsername(), message);
            errorStack.push(
                new Error(
                    null /* logins have no completion */, extractError(matcher), wireTrace.list()));
            disconnectAbnormally(message);
          }
        }
        return;
      }

      // Copy to local var as the value can change underneath us.
      FolderObserver observer = this.observer;
      if (idleRequested.get() || idleAcknowledged.get()) {
        synchronized (idleMutex) {
          if (IDLE_ENDED_REGEX.matcher(message).matches()) {
            idleRequested.compareAndSet(true, false);
            idleAcknowledged.set(false);

            // Now fire the events.
            PushedData data = pushedData;
            pushedData = null;

            idler.idleEnd();
            observer.changed(
                data.pushAdds.isEmpty() ? null : data.pushAdds,
                data.pushRemoves.isEmpty() ? null : data.pushRemoves);
            return;
          }

          // Queue up any push notifications to publish to the client in a second.
          Matcher existsMatcher = IDLE_EXISTS_REGEX.matcher(message);
          boolean matched = false;
          if (existsMatcher.matches()) {
            int number = Integer.parseInt(existsMatcher.group(1));
            pushedData.pushAdds.add(number);
            pushedData.pushRemoves.remove(number);
            matched = true;
          } else {
            Matcher expungeMatcher = IDLE_EXPUNGE_REGEX.matcher(message);
            if (expungeMatcher.matches()) {
              int number = Integer.parseInt(expungeMatcher.group(1));
              pushedData.pushRemoves.add(number);
              pushedData.pushAdds.remove(number);
              matched = true;
            }
          }

          // Stop idling, when we get the idle ended message (next cycle) we can publish what's been
          // gathered.
          if (matched) {
            if (!pushedData.idleExitSent) {
              idler.done();
              pushedData.idleExitSent = true;
            }
            return;
          }
        }
      }

      complete(message);
    } catch (Exception ex) {
      CommandCompletion completion = completions.poll();
      if (completion != null) completion.error(message, ex);
      else {
        log.error(
            "Strange exception during mail processing (no completions available!): {}",
            message,
            ex);
        errorStack.push(new Error(null, "No completions available!", wireTrace.list()));
      }
      throw ex;
    }
  }
コード例 #22
0
  /** @throws Exception Thrown if test failed. */
  public void testD() throws Exception {
    final Collection<SampleBean> set = new GridConcurrentWeakHashSet<>();

    final int cnt = 100;

    final CountDownLatch start = new CountDownLatch(1);
    final CountDownLatch stop = new CountDownLatch(3);

    new Thread() {
      @Override
      public void run() {
        try {
          start.await();

          for (int i = 0; i < cnt; i++) {
            for (int j = 0; j < cnt; j++) set.add(new SampleBean(i));
          }
        } catch (Exception e) {
          error(e.getMessage());
        }

        stop.countDown();
      }
    }.start();

    new Thread() {
      @Override
      public void run() {
        try {
          start.await();

          for (int i = 0; i < cnt; i++) {
            for (int j = 0; j < cnt; j++) set.remove(new SampleBean(i));
          }
        } catch (Exception e) {
          error(e.getMessage());
        }

        stop.countDown();
      }
    }.start();

    new Thread() {
      @SuppressWarnings({"UnusedDeclaration"})
      @Override
      public void run() {
        try {
          start.await();

          while (stop.getCount() > 1) {
            for (SampleBean b : set) {
              // No-op.
            }
          }
        } catch (Exception e) {
          error(e.getMessage());
        }

        stop.countDown();
      }
    }.start();

    start.countDown();

    stop.await();

    gc();

    assert set.isEmpty();
  }
コード例 #23
0
 public void terminate() {
   if (snapshotLatch != null) {
     while (snapshotLatch.getCount() > 0) snapshotLatch.countDown();
   }
 }
コード例 #24
0
 /** Count down wait latch. */
 private void releaseWaitLatch() {
   waitLatch.countDown();
 }