private void doTestRequestTimeout(boolean isPersistentSearch) throws Exception {
    InetSocketAddress address = TestCaseUtils.findFreeSocketAddress();

    /*
     * Use a mock server implementation which will ignore incoming requests
     * and leave the client waiting forever for a response.
     */
    @SuppressWarnings("unchecked")
    LDAPListener listener =
        new LDAPListener(
            address, Connections.newServerConnectionFactory(mock(RequestHandler.class)));

    /*
     * Use a very long time out in order to prevent the timeout thread from
     * triggering the timeout.
     */
    LDAPConnectionFactory factory =
        new LDAPConnectionFactory(
            address.getHostName(),
            address.getPort(),
            Options.defaultOptions()
                .set(TIMEOUT_IN_MILLISECONDS, TimeUnit.SECONDS.toMillis((long) 100)));
    GrizzlyLDAPConnection connection = (GrizzlyLDAPConnection) factory.getConnection();
    try {
      SearchRequest request =
          Requests.newSearchRequest("dc=test", SearchScope.BASE_OBJECT, "(objectClass=*)");
      if (isPersistentSearch) {
        request.addControl(PersistentSearchRequestControl.newControl(true, true, true));
      }
      SearchResultHandler searchHandler = mock(SearchResultHandler.class);
      @SuppressWarnings("unchecked")
      ExceptionHandler<LdapException> exceptionHandler = mock(ExceptionHandler.class);
      connection.searchAsync(request, searchHandler).thenOnException(exceptionHandler);

      // Pass in a time which is guaranteed to trigger expiration.
      connection.handleTimeout(System.currentTimeMillis() + 1000000);
      if (isPersistentSearch) {
        verifyZeroInteractions(searchHandler);
      } else {
        ArgumentCaptor<LdapException> arg = ArgumentCaptor.forClass(LdapException.class);
        verify(exceptionHandler).handleException(arg.capture());
        assertThat(arg.getValue()).isInstanceOf(TimeoutResultException.class);
        assertThat(arg.getValue().getResult().getResultCode())
            .isEqualTo(ResultCode.CLIENT_SIDE_TIMEOUT);
      }
    } finally {
      connection.close();
      listener.close();
      factory.close();
    }
  }
  /**
   * Search the Database Server using the provided search request.
   *
   * @param request The search request.
   * @param handler A search result handler which can be used to asynchronously process the search
   *     result entries and references as they are received, may be null.
   * @return The result of the operation.
   * @throws ErrorResultException If the result code indicates that the request failed for some
   *     reason.
   * @throws SQLException If the SQL query has an invalid format.
   * @throws NullPointerException If {@code request} was {@code null}, or if a corresponding mapping
   *     value could not be found in the mapping component.
   */
  @Override
  public Result search(SearchRequest request, SearchResultHandler handler)
      throws ErrorResultException {
    Result r;
    try {
      // Split up the DN the of the request.
      final DN DN = request.getName();
      final int DNSize = DN.size();
      final String baseDN = DN.parent(DNSize - 2).toString();
      RDN OU = null;
      RDN rDN = null;
      String OUName = "";
      String rDNAttributeName = "";
      String rDNAttributeValue = "";

      if (DNSize > 3) {
        OU = DN.parent(1).rdn();
        OUName = OU.getFirstAVA().getAttributeValue().toString();
        rDN = DN.parent(0).rdn();
        rDNAttributeName = rDN.getFirstAVA().getAttributeType().getNameOrOID();
        rDNAttributeValue = rDN.getFirstAVA().getAttributeValue().toString();
      } else if (DNSize > 2) {
        OU = DN.parent(0).rdn();
        OUName = OU.getFirstAVA().getAttributeValue().toString();
      }
      List<String> returnAttributeNames = request.getAttributes();
      final List<String> removeAttributesList = new ArrayList<String>();

      // Get all column names to be returned to return full records.
      if (returnAttributeNames == null) {
        if (OU != null) {
          returnAttributeNames = jdbcm.getOrganizationalUnitAttributes(baseDN, OUName);

          if (returnAttributeNames == null)
            throw new NullPointerException(
                "JDBC Error: Could not find any matching attributes in OU: '" + OUName + "'.");
          final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);

          for (Iterator<String> iterAttributes = returnAttributeNames.iterator();
              iterAttributes.hasNext(); ) {
            String returnAttributeName = iterAttributes.next();
            final String returnColumnName =
                jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, returnAttributeName);

            if (returnColumnName == null) removeAttributesList.add(returnAttributeName);
          }
          returnAttributeNames.removeAll(removeAttributesList);
        } else {
          final List<String> OUList = jdbcm.getOrganizationalUnits(baseDN);

          if (OUList == null)
            throw new NullPointerException(
                "JDBC Error: Could not find any matching organizational units in DN: '"
                    + baseDN
                    + "'.");

          for (Iterator<String> iterOU = OUList.iterator(); iterOU.hasNext(); ) {
            OUName = iterOU.next();
            final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);
            returnAttributeNames = jdbcm.getOrganizationalUnitAttributes(baseDN, OUName);

            for (Iterator<String> iterAttributes = returnAttributeNames.iterator();
                iterAttributes.hasNext(); ) {
              String returnAttributeName = iterAttributes.next();
              final String returnColumnName =
                  jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, returnAttributeName);

              if (returnColumnName == null) removeAttributesList.add(returnAttributeName);
            }
            returnAttributeNames.removeAll(removeAttributesList);
          }
        }
      }
      // Convert the search filter.
      final Filter searchFilter = request.getFilter();
      final String convertedFilterString = convertSearchFilter(searchFilter, baseDN, OUName);

      if (convertedFilterString.startsWith("Err:")) {
        String[] errorStringSplitter = convertedFilterString.split("Err:");
        throw new NullPointerException(errorStringSplitter[1]);
      }
      final List<String> returnColumnNames = new ArrayList<String>();

      // Search mapping for the corresponding column name for each return attribute.
      for (Iterator<String> iter = returnAttributeNames.iterator(); iter.hasNext(); ) {

        if (OU == null) {
          final String returnAttributeName = iter.next();
          final List<String> OUList = jdbcm.getOrganizationalUnits(baseDN);

          if (OUList == null)
            throw new NullPointerException(
                "JDBC Error: Could not find any matching organizational units in DN: '"
                    + baseDN
                    + "'.");

          for (Iterator<String> iterOU = OUList.iterator(); iterOU.hasNext(); ) {
            OUName = iterOU.next();
            final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);
            final String returnColumnName =
                jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, returnAttributeName);

            if (returnColumnName == null) continue;
            else returnColumnNames.add(returnColumnName);
            break;
          }
        } else {
          final String returnAttributeName = iter.next();
          final String tableName = jdbcm.getTableNameFromMapping(baseDN, OUName);

          if (tableName == null)
            throw new NullPointerException(
                "JDBC Error: Could not find matching table name for OU: '"
                    + OUName
                    + "' in DN: '"
                    + baseDN
                    + "'. Please check if mapping was succesful.");
          final String returnColumnName =
              jdbcm.getColumnNameFromMapping(tableName, baseDN, OUName, returnAttributeName);

          if (returnColumnName == null)
            throw new NullPointerException(
                "JDBC Error: Could not find matching column name for attribute: '"
                    + returnAttributeName
                    + "'. Please check if mapping was succesful.");
          returnColumnNames.add(returnColumnName);
        }
      }

      String selectString = "";
      String fromString = "";
      String whereString = convertedFilterString;

      if (returnAttributeNames.isEmpty()) selectString = "*";

      // Build the SQL SELECT string.
      if (OU == null) {
        final List<String> OUList = jdbcm.getOrganizationalUnits(baseDN);

        if (OUList == null)
          throw new NullPointerException(
              "JDBC Error: Could not find any matching organizational units in DN: '"
                  + baseDN
                  + "'.");

        for (int i = 0; i < OUList.size(); i++) {
          final String currentOU = OUList.get(i);
          final String currentTable = jdbcm.getTableNameFromMapping(baseDN, currentOU);

          if (currentTable == null) continue;
          final List<String> currentTableColumnNames = jdbcm.getTableColumns(currentTable);

          if (!fromString.isEmpty()) fromString = fromString.concat(",");
          fromString = fromString.concat(currentTable);

          for (int j = 0; j < returnColumnNames.size(); j++) {
            final String returnColumnName = returnColumnNames.get(j);

            if (currentTableColumnNames.contains(returnColumnName)) {

              if (!selectString.isEmpty()) selectString = selectString.concat(",");
              selectString = selectString.concat(currentTable + "." + returnColumnName);
            }
          }
        }
      } else {
        final String selectTable = jdbcm.getTableNameFromMapping(baseDN, OUName);
        if (selectTable == null)
          throw new NullPointerException(
              "JDBC Error: Could not find matching table name for OU: '"
                  + OUName
                  + "' in DN: '"
                  + baseDN
                  + "'. Please check if mapping was succesful.");

        fromString = fromString.concat(selectTable);

        for (int j = 0; j < returnColumnNames.size(); j++) {
          final String returnColumnName = returnColumnNames.get(j);

          if (returnColumnName == null) continue;

          if (!selectString.isEmpty()) selectString = selectString.concat(",");
          selectString = selectString.concat(returnColumnName);
        }

        if (rDN != null) {
          final String columnName =
              jdbcm.getColumnNameFromMapping(selectTable, baseDN, OUName, rDNAttributeName);

          if (columnName == null)
            throw new NullPointerException(
                "JDBC Error: Could not find matching column name for attribute: '"
                    + rDNAttributeName
                    + "'. Please check if mapping was succesful.");
          whereString = columnName + "='" + rDNAttributeValue + "'";
        }
      }

      // Build the SQL query.
      final Statement st = connection.createStatement();
      String sql = "SELECT " + selectString + " FROM " + fromString;

      if (!whereString.isEmpty()) sql = sql.concat(" WHERE " + whereString);
      ResultSet rs = st.executeQuery(sql);
      ResultSetMetaData rsmd = rs.getMetaData();

      // Fill the entries result for this query.
      searchEntries.clear();
      while (rs.next()) {
        Entry e = new LinkedHashMapEntry();

        for (int i = 1; i <= rsmd.getColumnCount(); i++) {
          e.addAttribute(rsmd.getTableName(i) + "_" + rsmd.getColumnName(i), rs.getObject(i));
        }
        searchEntries.add(e);
      }
      r = Responses.newResult(ResultCode.SUCCESS);
    } catch (SQLException e) {
      System.out.println(e.toString());
      r = Responses.newResult(ResultCode.OPERATIONS_ERROR);
      r.setCause(e);
    } catch (NullPointerException e) {
      System.out.println(e.toString());
      r = Responses.newCompareResult(ResultCode.OPERATIONS_ERROR);
      r.setCause(e);
    }
    return r;
  }