/**
   * Adds a summary message and a list of errors for each processed record that failed.
   *
   * @param context the processing context
   * @param msgBroker the message broker
   * @param resourceKey the resource key associated with the status type
   * @param statusType the sstatus type
   * @param count the count associated with the status type
   */
  private void addErrorMessages(
      ProcessingContext context,
      MessageBroker msgBroker,
      String resourceKey,
      ProcessedRecord.StatusType statusType,
      int count) {

    Object[] parameters = new Integer[] {count};
    String msg = msgBroker.retrieveMessage(resourceKey);
    msg = MessageFormat.format(msg, parameters);
    FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, null);
    msgBroker.addMessage(fm);
    for (ProcessedRecord processedRecord : context.getProcessedRecords()) {
      if (processedRecord.getStatusType().equals(statusType)
          && (processedRecord.getExceptions() != null)) {
        StringBuilder sb = new StringBuilder();
        sb.append(processedRecord.getSourceUri());
        if (processedRecord.getExceptions() != null) {
          for (String error : processedRecord.getExceptions()) {
            sb.append("<br />").append(error);
          }
        }
        fm = new FacesMessage(FacesMessage.SEVERITY_ERROR, sb.toString(), null);
        msgBroker.addMessage(fm);
      }
    }
  }
  /**
   * Executes a delete user action.
   *
   * @param request HTTP request.
   * @param response HTTP response.
   * @param context request context
   * @throws Exception if an exception occurs
   */
  private void executeDeleteUser(
      HttpServletRequest request, HttpServletResponse response, RequestContext context)
      throws Exception {
    try {
      String[] parts = request.getRequestURI().toString().split("/");
      if (parts.length > 0) {
        String userIdentifier = URLDecoder.decode(parts[5].trim(), "UTF-8");
        if (userIdentifier.endsWith(userDIT)) {
          String attempt = Val.chkStr(request.getParameter("attempt"));
          IdentityAdapter idAdapter = context.newIdentityAdapter();
          User user = new User();
          user.setDistinguishedName(userIdentifier);
          idAdapter.readUserProfile(user);
          idAdapter.readUserGroups(user);

          boolean isSelf = checkSelf(context, userIdentifier);
          if ((isSelf && attempt.equals("2")) || !isSelf) {
            idAdapter.deleteUser(user);
            response
                .getWriter()
                .write(msgBroker.retrieveMessage("catalog.identity.deleteUser.success"));
          } else {
            response.getWriter().write("prompt");
          }
        }
      }
    } finally {
    }
  }
  /**
   * Reads configured roles.
   *
   * @param request HTTP request.
   * @param response HTTP response.
   * @param context request context
   * @throws IdentityException if a system error occurs
   */
  private void executeReadConfigureRoles(
      HttpServletRequest request, HttpServletResponse response, RequestContext context)
      throws Exception {
    String mimeType = "application/json";
    String rolesJson = " { \"configuredRoles\" : [";
    Roles roles = buildSelectableRoles(context);
    ArrayList<String> sortedKeys = new ArrayList<String>(roles.keySet());
    Collections.sort(sortedKeys);
    boolean firstRole = true;
    for (int i = 0; i < sortedKeys.size(); i++) {
      Role role = roles.get(sortedKeys.get(i));
      String roleDn = Val.chkStr(role.getDistinguishedName());
      String roleKey = Val.chkStr(role.getKey());
      String roleName = msgBroker.retrieveMessage(Val.chkStr(role.getResKey()));
      if (!role.isManage()) continue;
      if (!firstRole) {
        rolesJson += ",";
      } else {
        firstRole = false;
      }
      rolesJson +=
          " { \"roleName\" : \""
              + Val.escapeStrForJson(roleName)
              + "\" , \"roleDn\" : \""
              + Val.escapeStrForJson(roleDn)
              + "\" , \"roleKey\" : \""
              + Val.escapeStrForJson(roleKey)
              + "\" }";
    }
    rolesJson += " ] } ";

    writeCharacterResponse(response, rolesJson, "UTF-8", mimeType + ";charset=UTF-8");
  }
 @Override
 public OpenSearchProperties getOpenSearchProperties() {
   OpenSearchProperties osProps = new OpenSearchProperties();
   osProps.setShortName(msgBroker.retrieveMessage("catalog.openSearch.shortName"));
   osProps.setNumberOfHits(query.getResult().getNumberOfHits());
   osProps.setStartRecord(query.getFilter().getStartRecord());
   osProps.setRecordsPerPage(query.getResult().getNumberOfHits());
   return osProps;
 }
  /**
   * Executes a remove member action.
   *
   * @param request HTTP request.
   * @param response HTTP response.
   * @param context request context
   * @throws Exception if an exception occurs
   */
  protected void executeRemoveMember(
      HttpServletRequest request, HttpServletResponse response, RequestContext context)
      throws Exception {
    try {
      String[] parts = request.getRequestURI().toString().split("/");
      String member = Val.chkStr(request.getParameter("member"));
      String attempt = Val.chkStr(request.getParameter("attempt"));
      IdentityAdapter idAdapter = context.newIdentityAdapter();
      User user = new User();
      user.setDistinguishedName(member);
      idAdapter.readUserProfile(user);
      if (parts.length > 0) {
        String groupIdentifier = URLDecoder.decode(parts[5].trim(), "UTF-8");
        if (!groupIdentifier.endsWith(groupDIT)) {
          IdentityConfiguration idConfig = context.getIdentityConfiguration();
          Roles configuredRoles = idConfig.getConfiguredRoles();
          Role roleRegistered = configuredRoles.get(groupIdentifier);
          groupIdentifier = roleRegistered.getDistinguishedName();
        }
        boolean isSelf = checkSelf(context, member);
        if ((isSelf && attempt.equals("2")) || !isSelf) {

          boolean checkGroupConfigured = true;
          if (checkIfAllowConfigured(context)) {
            checkGroupConfigured = checkIfConfigured(context, groupIdentifier);
          }
          boolean isAllowedToManage = true;
          isAllowedToManage = checkIfAllowedToManage(context, groupIdentifier);
          if (checkGroupConfigured) {
            if (isAllowedToManage) {
              idAdapter.removeUserFromGroup(user, groupIdentifier);
              response
                  .getWriter()
                  .write(msgBroker.retrieveMessage("catalog.identity.removeRole.success"));
            } else {
              response.sendError(
                  HttpServletResponse.SC_BAD_REQUEST,
                  "{ \"error\":\""
                      + groupIdentifier
                      + " is not allowed to be managed in geoportal. \"}");
              return;
            }
          } else {
            response.sendError(
                HttpServletResponse.SC_BAD_REQUEST,
                "{ \"error\":\"" + groupIdentifier + " is not configured in geoportal. \"}");
            return;
          }

        } else {
          response.getWriter().write("prompt");
        }
      }
    } finally {
    }
  }
 /**
  * Adds a summary message and a list processed records for a processd status type.
  *
  * @param context the processing context
  * @param msgBroker the message broker
  * @param resourceKey the resource key associated with the status type
  * @param statusType the sstatus type
  * @param count the count associated with the status type
  */
 private void addSummaryMessage(
     ProcessingContext context,
     MessageBroker msgBroker,
     String resourceKey,
     ProcessedRecord.StatusType statusType,
     int count) {
   Object[] parameters = new Integer[] {count};
   String msg = msgBroker.retrieveMessage(resourceKey);
   msg = MessageFormat.format(msg, parameters);
   StringBuilder sb = new StringBuilder(msg);
   for (ProcessedRecord processedRecord : context.getProcessedRecords()) {
     if (processedRecord.getStatusType().equals(statusType)) {
       sb.append("<br />").append(processedRecord.getSourceUri());
     }
   }
   if (sb.length() > 0) {
     FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_INFO, sb.toString(), null);
     msgBroker.addMessage(fm);
   }
 }
  /**
   * Serializes user information from ldap to json string.
   *
   * @param context request context
   * @param user the user to be serialized
   * @return the user profile information serialized as json string.
   * @throws IdentityException if a system error occurs preventing the action
   * @throws NamingException if an LDAP naming exception occurs
   */
  protected String serializeUserAsJson(RequestContext context, User user)
      throws IdentityException, NamingException {
    String usersJson = "{ \"attributes\": [";
    UserAttributeMap attributes = user.getProfile();
    boolean first = true;
    List<String> sortedKeys = new ArrayList<String>(attributes.keySet());
    // Collections.sort(sortedKeys); TODO to sort or not ?
    for (int i = 0; i < sortedKeys.size(); i++) {
      UserAttribute attr = attributes.get(sortedKeys.get(i));
      String key =
          Val.chkStr(msgBroker.retrieveMessage("catalog.identity.profile.label." + attr.getKey()));
      String value = "";
      value = Val.chkStr(attr.getValue());
      if (attr.getKey().equalsIgnoreCase("password")) continue;
      if (!first) {
        usersJson += ",";
      } else {
        first = false;
      }
      usersJson +=
          " { \"key\" : \""
              + Val.escapeStrForJson(key)
              + "\" , \"value\" : \""
              + Val.escapeStrForJson(value)
              + "\" }";
    }
    usersJson += " ] , ";

    usersJson += " \"userDn\" : \"" + user.getDistinguishedName() + " \" , ";

    String groupsJson = " \"groups\" : [";
    Groups groups = user.getGroups();
    groups.sort();
    boolean firstGroup = true;
    for (Group group : groups.values()) {
      String gkey = Val.chkStr(group.getKey());
      String name = Val.chkStr(group.getName());
      String dn = Val.chkStr(group.getDistinguishedName());
      if (!firstGroup) {
        groupsJson += ",";
      } else {
        firstGroup = false;
      }
      groupsJson +=
          " { \"key\" : \""
              + Val.escapeStrForJson(gkey)
              + "\" , \"name\" : \""
              + Val.escapeStrForJson(name)
              + "\" , \"dn\" : \""
              + Val.escapeStrForJson(dn)
              + "\" }";
    }
    groupsJson += " ] , ";

    String rolesJson = " \"selectableRoles\" : [";
    Roles roles = buildSelectableRoles(context);
    sortedKeys = new ArrayList<String>(roles.keySet());
    Collections.sort(sortedKeys);
    boolean firstRole = true;
    for (int i = 0; i < sortedKeys.size(); i++) {
      Role role = roles.get(sortedKeys.get(i));
      String roleDn = Val.chkStr(role.getDistinguishedName());
      String roleKey = Val.chkStr(role.getKey());
      String roleName = msgBroker.retrieveMessage(Val.chkStr(role.getResKey()));
      if (!role.isManage()) continue;
      boolean hasRole = false;
      for (Group group : groups.values()) {
        String groupDn = Val.chkStr(group.getDistinguishedName());
        if (roleDn.equals(groupDn)) {
          hasRole = true;
          break;
        }
      }
      if (!firstRole) {
        rolesJson += ",";
      } else {
        firstRole = false;
      }
      rolesJson +=
          " { \"roleName\" : \""
              + Val.escapeStrForJson(roleName)
              + "\" , \"roleDn\" : \""
              + Val.escapeStrForJson(roleDn)
              + "\" , \"roleKey\" : \""
              + Val.escapeStrForJson(roleKey)
              + "\" , \"hasRole\" : \""
              + hasRole
              + "\" }";
    }
    rolesJson += " ] } ";
    String json = usersJson + groupsJson + rolesJson;
    return json;
  }
  /**
   * Handles a metadata file upload action. <br>
   * This is the default entry point for a sub-class of BaseActionListener. <br>
   * This BaseActionListener handles the JSF processAction method and invokes the processSubAction
   * method of the sub-class.
   *
   * @param event the associated JSF action event
   * @param context the context associated with the active request
   * @throws AbortProcessingException if processing should be aborted
   * @throws Exception if an exception occurs
   */
  @Override
  protected void processSubAction(ActionEvent event, RequestContext context)
      throws AbortProcessingException, Exception {

    // initialize
    MessageBroker msgBroker = extractMessageBroker();
    String sFileName = "";
    String sXml = "";
    UIComponent component = event.getComponent();
    String sCommand = Val.chkStr((String) component.getAttributes().get("command"));
    boolean bValidateOnly = sCommand.equalsIgnoreCase("validate");
    boolean bIsBrowse =
        this.getSpecificationMethod().equals(UploadMetadataController.SPECIFICATIONMETHOD_BROWSE);
    String sExplicitPath = this.getExplicitPath();

    try {

      // upload a single file from disk
      if (bIsBrowse) {
        FileItem item = extractFileItem();
        if (item != null) {
          sFileName = Val.chkStr(item.getName());
          if (sFileName.length() > 0) {
            File file = new File(sFileName);
            sFileName = file.getName();
          }
          sXml = extractItemXml(item);
        }
        if (sFileName.length() > 0) {
          FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_WARN, sFileName, null);
          msgBroker.addMessage(fm);
        }

        if (sFileName.length() == 0) {
          msgBroker.addErrorMessage("publication.uploadMetadata.err.file.required");
        } else if (sXml.length() == 0) {
          msgBroker.addErrorMessage("publication.uploadMetadata.err.file.empty");
        } else if (bValidateOnly) {
          ValidationRequest request = new ValidationRequest(context, sFileName, sXml);
          request.verify();
          msgBroker.addSuccessMessage("catalog.publication.success.validated");
        } else {
          Publisher publisher = getSelectablePublishers().selectedAsPublisher(context, false);
          UploadRequest request = new UploadRequest(context, publisher, sFileName, sXml);
          request.publish();
          if (request.getPublicationRecord().getWasDocumentUnchanged()) {
            msgBroker.addSuccessMessage("publication.success.unchanged");
          } else if (request.getPublicationRecord().getWasDocumentReplaced()) {
            msgBroker.addSuccessMessage("publication.success.replaced");
          } else {
            msgBroker.addSuccessMessage("publication.success.created");
          }
        }

        // handle an empty explicit url or network path
      } else if (sExplicitPath.length() == 0) {
        msgBroker.addErrorMessage("publication.uploadMetadata.err.file.required");

        // process an explicit url or network path
      } else {
        FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_WARN, sExplicitPath, null);
        msgBroker.addMessage(fm);

        sFileName = sExplicitPath;
        Publisher publisher = getSelectablePublishers().selectedAsPublisher(context, false);
        HttpClientRequest httpClient = HttpClientRequest.newRequest();

        ProcessingContext pContext =
            new ProcessingContext(context, publisher, httpClient, null, bValidateOnly);
        pContext.setMessageBroker(msgBroker);
        ProcessorFactory factory = new ProcessorFactory();
        ResourceProcessor processor = factory.interrogate(pContext, sExplicitPath);

        if (processor == null) {
          throw new IOException("Unable to process resource.");
        }
        processor.process();
        boolean wasSingleSource = pContext.getWasSingleSource();

        // summary messages
        if (bValidateOnly) {

          if (wasSingleSource && (pContext.getNumberValidated() == 1)) {
            msgBroker.addSuccessMessage("catalog.publication.success.validated");
          } else if (pContext.getNumberValidated() > 0) {
            addSummaryMessage(
                pContext,
                msgBroker,
                "catalog.publication.uploadMetadata.summary.valid",
                ProcessedRecord.StatusType.VALIDATED,
                pContext.getNumberValidated());
          }
          if (wasSingleSource && (pContext.getNumberFailed() == 1)) {
            Exception lastException = pContext.getLastException();
            if (pContext.getLastException() != null) {
              throw lastException;
            } else {
              // TODO message here ??
            }
          } else if (pContext.getNumberFailed() > 0) {
            addErrorMessages(
                pContext,
                msgBroker,
                "catalog.publication.uploadMetadata.summary.invalid",
                ProcessedRecord.StatusType.FAILED,
                pContext.getNumberFailed());
          }
          if ((pContext.getNumberValidated() == 0) && (pContext.getNumberFailed() == 0)) {
            msgBroker.addErrorMessage(
                "catalog.publication.uploadMetadata.summary.valid", new Integer[] {0});
          }

          // publication related messages
        } else {

          if (wasSingleSource && (pContext.getNumberCreated() == 1)) {
            msgBroker.addSuccessMessage("publication.success.created");
          } else if (pContext.getNumberCreated() > 0) {
            addSummaryMessage(
                pContext,
                msgBroker,
                "catalog.publication.uploadMetadata.summary.created",
                ProcessedRecord.StatusType.CREATED,
                pContext.getNumberCreated());
          }
          if (wasSingleSource && (pContext.getNumberReplaced() == 1)) {
            msgBroker.addSuccessMessage("publication.success.replaced");
          } else if (pContext.getNumberReplaced() > 0) {
            addSummaryMessage(
                pContext,
                msgBroker,
                "catalog.publication.uploadMetadata.summary.replaced",
                ProcessedRecord.StatusType.REPLACED,
                pContext.getNumberReplaced());
          }
          if (wasSingleSource && (pContext.getNumberUnchanged() == 1)) {
            msgBroker.addSuccessMessage("publication.success.unchanged");
          } else if (pContext.getNumberUnchanged() > 0) {
            addSummaryMessage(
                pContext,
                msgBroker,
                "catalog.publication.uploadMetadata.summary.unchanged",
                ProcessedRecord.StatusType.UNCHNAGED,
                pContext.getNumberUnchanged());
          }
          if (pContext.getNumberDeleted() > 0) {
            addSummaryMessage(
                pContext,
                msgBroker,
                "catalog.publication.uploadMetadata.summary.deleted",
                ProcessedRecord.StatusType.DELETED,
                pContext.getNumberDeleted());
          }

          if (wasSingleSource && (pContext.getNumberFailed() == 1)) {
            Exception lastException = pContext.getLastException();
            if (pContext.getLastException() != null) {
              throw lastException;
            } else {
              // TODO message here ??
            }
          } else if (pContext.getNumberFailed() > 0) {
            addErrorMessages(
                pContext,
                msgBroker,
                "catalog.publication.uploadMetadata.summary.failed",
                ProcessedRecord.StatusType.FAILED,
                pContext.getNumberFailed());
          }
        }
      }

      // handle a validation exception
    } catch (ValidationException e) {

      String sKey = e.getKey();
      if (sKey.length() > 0) {
        String sMsg = sKey;
        Schema schema = context.getCatalogConfiguration().getConfiguredSchemas().get(sKey);
        if (schema != null) {
          if (schema.getLabel() != null) {
            String sResKey = schema.getLabel().getResourceKey();
            if (sResKey.length() > 0) {
              sMsg = extractMessageBroker().retrieveMessage(sResKey) + " (" + sKey + ")";
            }
          }
        }
        FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_WARN, " - " + sMsg, null);
        extractMessageBroker().addMessage(fm);
      }

      e.getValidationErrors().buildMessages(msgBroker, true);

      // handle remaining exceptions
    } catch (Exception e) {

      // there seems to be no good exception related to a file that is simply
      // not an XML file, a message containing "content is not allowed in prolog"
      // seems to be the best guess at the moment
      String sMsg = e.toString().toLowerCase();
      if (sMsg.indexOf("content is not allowed in prolog") != -1) {
        msgBroker.addErrorMessage("publication.uploadMetadata.err.file.prolog");
      } else {
        throw e;
      }
    }
  }
  /**
   * Validates entered content.
   *
   * @param mb message broker
   * @return <code>true</code> if data is valid
   */
  public boolean validate(MessageBroker mb) {
    _valid = true;

    if (getHostUrl().length() == 0) {
      mb.addErrorMessage("catalog.harvest.manage.edit.err.hostUrlReq");
      _valid = false;
    }

    String kind = _harvestRepository.getProtocol().getKind();
    if (kind.equalsIgnoreCase(ProtocolType.ArcIms.name())) {
      if (getArcIms().getPortNoAsString().length() == 0) {
        mb.addErrorMessage("catalog.harvest.manage.edit.err.portNumberReq");
        _valid = false;
      } else {
        try {
          int portNo = Integer.parseInt(getArcIms().getPortNoAsString());
          if (!(portNo >= 0 && portNo < 65536)) {
            mb.addErrorMessage("catalog.harvest.manage.edit.err.portNumberInv");
            _valid = false;
          }
        } catch (NumberFormatException ex) {
          mb.addErrorMessage("catalog.harvest.manage.edit.err.portNumberInv");
          _valid = false;
        }
      }
      if (getArcIms().getServiceName().length() == 0) {
        mb.addErrorMessage("catalog.harvest.manage.edit.err.serviceNameReq");
        _valid = false;
      }
    } else if (kind.equalsIgnoreCase(ProtocolType.OAI.name())) {
      if (getOai().getPrefix().length() == 0) {
        mb.addErrorMessage("catalog.harvest.manage.edit.err.prefixReq");
        _valid = false;
      }
    } else if (kind.equalsIgnoreCase("arcgis")) {
      ArcGISProtocol p = (ArcGISProtocol) protocols.get("arcgis");
      if (p.getSoapUrl().length() == 0) {
        mb.addErrorMessage("catalog.harvest.manage.edit.err.soapUrl");
        _valid = false;
      }
    } else if (kind.equalsIgnoreCase("agp2agp")) {
      HarvestProtocolAgp2Agp p = (HarvestProtocolAgp2Agp) protocols.get("agp2agp");
      if (p != null) {
        if (!getArcgisDotComAllowed()) {
          if (p.getDestinationHost().toLowerCase().endsWith("arcgis.com")
              || p.getDestinationHost().toLowerCase().endsWith("arcgisonline.com")) {
            mb.addErrorMessage("catalog.harvest.manage.test.msg.agp2agp.arcgis.forbiden");
            _valid = false;
          }
        } else if (!getCrossAllowed()) {
          String srcHost[] = p.getSourceHost().split("[.]");
          String dstHost[] = p.getDestinationHost().split("[.]");

          if (srcHost != null && srcHost.length >= 2 && dstHost != null && dstHost.length >= 2) {
            if (srcHost[srcHost.length - 1].equalsIgnoreCase(dstHost[dstHost.length - 1])
                && srcHost[srcHost.length - 2].equalsIgnoreCase(dstHost[dstHost.length - 2])) {
              mb.addErrorMessage("catalog.harvest.manage.test.msg.agp2agp.cross.forbiden");
              _valid = false;
            }
          }
        }
        if (_valid) {
          if (!p.getSourceHost().matches(HOST_NAME_REGEX)) {
            mb.addErrorMessage("catalog.harvest.manage.edit.src.h.err");
            _valid = false;
          }
          if (p.getAttributeMap().getValue("src-q").isEmpty()) {
            mb.addErrorMessage("catalog.harvest.manage.edit.src.q.err");
            _valid = false;
          }
          if (p.getAttributeMap().getValue("src-m").isEmpty()) {
            mb.addErrorMessage("catalog.harvest.manage.edit.src.m.err");
            _valid = false;
          } else if (Val.chkLong(p.getAttributeMap().getValue("src-m"), 0) <= 0
              || Val.chkLong(p.getAttributeMap().getValue("src-m"), 0)
                  > HarvestProtocolAgp2Agp.getAgp2AgpMaxItems()) {
            mb.addErrorMessage(
                "catalog.harvest.manage.edit.src.m.err.less",
                new Object[] {HarvestProtocolAgp2Agp.getAgp2AgpMaxItems()});
            _valid = false;
          }
          if (p.getAttributeMap().getValue("src-u").isEmpty()) {
            mb.addErrorMessage("catalog.harvest.manage.edit.src.u.err");
            _valid = false;
          }
          if (p.getAttributeMap().getValue("src-p").isEmpty()) {
            mb.addErrorMessage("catalog.harvest.manage.edit.src.p.err");
            _valid = false;
          }

          if (!p.getDestinationHost().matches(HOST_NAME_REGEX)) {
            mb.addErrorMessage("catalog.harvest.manage.edit.dest.h.err");
            _valid = false;
          }
          if (p.getAttributeMap().getValue("dest-o").isEmpty()) {
            mb.addErrorMessage("catalog.harvest.manage.edit.dest.o.err");
            _valid = false;
          }
          if (p.getAttributeMap().getValue("dest-u").isEmpty()) {
            mb.addErrorMessage("catalog.harvest.manage.edit.dest.u.err");
            _valid = false;
          }
          if (p.getAttributeMap().getValue("dest-p").isEmpty()) {
            mb.addErrorMessage("catalog.harvest.manage.edit.dest.p.err");
            _valid = false;
          }
          if (p.getAttributeMap().getValue("dest-f").isEmpty()) {
            mb.addErrorMessage("catalog.harvest.manage.edit.dest.f.err");
            _valid = false;
          }
        }
      }
    }

    return _valid;
  }
  /**
   * Performs search operation.
   *
   * @param request HTTP servlet request
   * @param response HTTP servlet response
   * @param context request context
   * @param query query
   * @return records
   * @throws Exception if searching fails
   */
  @Override
  public IFeedRecords doSearch(
      HttpServletRequest request,
      HttpServletResponse response,
      RequestContext context,
      RestQuery query)
      throws Exception {
    MessageBroker msgBroker = new FacesContextBroker(request, response).extractMessageBroker();
    final Map<DiscoveredRecord, Map<String, List<String>>> mapping =
        new HashMap<DiscoveredRecord, Map<String, List<String>>>();

    List<IFeedRecords.FieldMeta> fields = new ArrayList<IFeedRecords.FieldMeta>();
    loadStdFieldMeta(fields);

    int startRecord = query.getFilter().getStartRecord();
    boolean returnIdsOnly = Val.chkBool(Val.chkStr(request.getParameter("returnIdsOnly")), false);
    if (returnIdsOnly) {
      startRecord = 1;
      query.getFilter().setMaxRecords(1);
      LuceneQueryAdapter tmp = new LuceneQueryAdapter();
      tmp.execute(context, query);
      query.getFilter().setMaxRecords(query.getResult().getNumberOfHits());
    }

    query.getFilter().setStartRecord(startRecord);
    LuceneQueryAdapter lqa =
        new LuceneQueryAdapter() {
          @Override
          protected void onRecord(DiscoveredRecord record, Document document) {
            Map<String, List<String>> fieldMap = new HashMap<String, List<String>>();
            for (Fieldable field : document.getFields()) {
              String name = field.name();
              List<String> fieldValues = fieldMap.get(name);
              if (fieldValues == null) {
                fieldValues = new ArrayList<String>();
                fieldMap.put(name, fieldValues);
              }
              fieldValues.add(field.stringValue());
            }
            mapping.put(record, fieldMap);
          }
        };
    lqa.execute(context, query);
    startRecord += query.getFilter().getMaxRecords();

    loadLuceneMeta(context, fields);

    OpenSearchProperties osProps = new OpenSearchProperties();
    osProps.setShortName(msgBroker.retrieveMessage("catalog.openSearch.shortName"));
    osProps.setNumberOfHits(query.getResult().getNumberOfHits());
    osProps.setStartRecord(query.getFilter().getStartRecord());
    osProps.setRecordsPerPage(query.getFilter().getMaxRecords());

    ResourceIdentifier resourceIdentifier = ResourceIdentifier.newIdentifier(context);
    DiscoveredRecordsAdapter discoveredRecordsAdapter =
        new DiscoveredRecordsAdapter(
            resourceIdentifier, osProps, fields, query.getResult().getRecords(), mapping);

    FeedLinkBuilder linkBuilder =
        new FeedLinkBuilder(context, RequestContext.resolveBaseContextPath(request), msgBroker);
    for (IFeedRecord record : discoveredRecordsAdapter) {
      linkBuilder.build(record);
    }

    return discoveredRecordsAdapter;
    /*
    MessageBroker msgBroker = new FacesContextBroker(request, response).extractMessageBroker();
    FeedLinkBuilder linkBuilder = new FeedLinkBuilder(RequestContext.resolveBaseContextPath(request), msgBroker);

    DcatRecordsAdapter discoveredRecordsAdapter = new DcatRecordsAdapter(msgBroker, linkBuilder, context, query);

    return discoveredRecordsAdapter;
    */
  }