/** @param maxResults Can be <code>null</code>, then {@link #getDefaultMaxResults()} is used. */
  public Browse(
      Service service,
      String objectID,
      BrowseFlag flag,
      String filter,
      long firstResult,
      Long maxResults,
      SortCriterion... orderBy) {

    super(new ActionInvocation(service.getAction("Browse")));

    log.fine("Creating browse action for object ID: " + objectID);

    getActionInvocation().setInput("ObjectID", objectID);
    getActionInvocation().setInput("BrowseFlag", flag.toString());
    getActionInvocation().setInput("Filter", filter);
    getActionInvocation().setInput("StartingIndex", new UnsignedIntegerFourBytes(firstResult));
    getActionInvocation()
        .setInput(
            "RequestedCount",
            new UnsignedIntegerFourBytes(maxResults == null ? getDefaultMaxResults() : maxResults));
    getActionInvocation().setInput("SortCriteria", SortCriterion.toString(orderBy));
  }
 @Override
 public BrowseResult browse(
     String objectID,
     BrowseFlag browseFlag,
     String filter,
     long firstResult,
     long maxResults,
     SortCriterion[] orderBy)
     throws ContentDirectoryException {
   if (MyTunesRssDIDL.getClientProfile() == null || MyTunesRssDIDL.getUser() == null) {
     LOGGER.trace(
         "No client profile found for client at "
             + AbstractContentDirectoryService.REMOTE_CLIENT_INFO
                 .get()
                 .getRemoteAddress()
                 .getHostAddress()
             + ".");
     throw new ContentDirectoryException(
         ErrorCode.ACTION_NOT_AUTHORIZED,
         "No client profile found for client at "
             + AbstractContentDirectoryService.REMOTE_CLIENT_INFO
                 .get()
                 .getRemoteAddress()
                 .getHostAddress()
             + ".");
   }
   LOGGER.debug(
       "Received browse request [objectID=\"{}\", browseFlag=\"{}\", filter=\"{}\", firstResult={}, maxResults={}, orderBy=\"{}\", userAgent=\"{}\"].",
       new Object[] {
         objectID,
         browseFlag,
         filter,
         firstResult,
         maxResults,
         orderBy,
         REMOTE_CLIENT_INFO.get().getRequestUserAgent()
       });
   Class<? extends MyTunesRssDIDL> contentClass = contentForOid.get(objectID);
   if (contentClass == null) {
     for (Map.Entry<String, Class<? extends MyTunesRssDIDL>> entry :
         contentForOidPrefix.entrySet()) {
       if (objectID.startsWith(entry.getKey() + ";")) {
         contentClass = entry.getValue();
         break;
       }
     }
     if (contentClass == null) {
       contentClass = RootMenuDIDL.class;
     }
   }
   try {
     MyTunesRssDIDL content = contentClass.newInstance();
     int separatorIndex = objectID.indexOf(';');
     String oidParams =
         separatorIndex > 0 && separatorIndex < objectID.length() - 1
             ? objectID.substring(separatorIndex + 1)
             : null;
     if (browseFlag == BrowseFlag.DIRECT_CHILDREN) {
       content.initDirectChildren(oidParams, firstResult, maxResults);
     } else if (browseFlag == BrowseFlag.METADATA) {
       content.initMetaData(oidParams);
     } else {
       LOGGER.warn("Unexpected browse flag \"" + browseFlag.name() + "\".");
       throw new ContentDirectoryException(
           ErrorCode.ARGUMENT_VALUE_INVALID,
           "Unexpected browse flag \"" + browseFlag.name() + "\".");
     }
     try {
       return new BrowseResult(
           new DIDLParser().generate(content),
           content.getCount(),
           content.getTotalMatches(),
           getSystemUpdateID().getValue());
     } catch (Exception e) {
       LOGGER.error("Could not create browse result.", e);
       throw new ContentDirectoryException(
           ErrorCode.ACTION_FAILED, "Could not create browse result: " + e.getMessage());
     }
   } catch (RuntimeException | InstantiationException | IllegalAccessException | SQLException e) {
     LOGGER.error("Could not create browse result.", e);
     throw new ContentDirectoryException(
         ErrorCode.ACTION_FAILED, "Could not create browse result: " + e.getMessage());
   }
 }