@Override
  public List<SecretAuthenticationKey> authenticate(
      @WebParam(name = "authenticationData", targetNamespace = "")
          List<AuthenticationTriple> authenticationData)
      throws AuthenticationExceptionException, SNAAExceptionException {

    Map<String, Set<AuthenticationTriple>> intersectionPrefixSet =
        getIntersectionPrefixSetAT(authenticationData);

    Set<Future<List<SecretAuthenticationKey>>> futures =
        new HashSet<Future<List<SecretAuthenticationKey>>>();

    for (String wsEndpointUrl : intersectionPrefixSet.keySet()) {
      AuthenticationCallable authenticationCallable =
          new AuthenticationCallable(
              wsEndpointUrl,
              new ArrayList<AuthenticationTriple>(intersectionPrefixSet.get(wsEndpointUrl)));
      Future<List<SecretAuthenticationKey>> future = executorService.submit(authenticationCallable);
      futures.add(future);
    }

    List<SecretAuthenticationKey> resultSet = new LinkedList<SecretAuthenticationKey>();

    for (Future<List<SecretAuthenticationKey>> future : futures) {
      try {
        resultSet.addAll(future.get());
      } catch (InterruptedException e) {
        SNAAException exception = new SNAAException();
        exception.setMessage(e.getMessage());
        throw new SNAAExceptionException(e.getMessage(), exception, e);
      } catch (ExecutionException e) {
        SNAAException exception = new SNAAException();
        exception.setMessage(e.getMessage());
        throw new SNAAExceptionException(e.getMessage(), exception, e);
      }
    }

    return resultSet;
  }
  @Override
  public boolean isAuthorized(
      @WebParam(name = "authenticationData", targetNamespace = "")
          List<SecretAuthenticationKey> authenticationData,
      @WebParam(name = "action", targetNamespace = "") Action action)
      throws SNAAExceptionException {

    if (authenticationData == null || action == null) {
      throw createSNAAException("Arguments must not be null!");
    }

    Map<String, Set<SecretAuthenticationKey>> intersectionPrefixSet =
        getIntersectionPrefixSetSAK(authenticationData);

    Set<Future<Boolean>> futures = new HashSet<Future<Boolean>>();

    for (String urnPrefix : intersectionPrefixSet.keySet()) {
      IsAuthorizedCallable authenticationCallable =
          new IsAuthorizedCallable(
              getWsnUrlFromUrnPrefix(urnPrefix),
              new ArrayList<SecretAuthenticationKey>(intersectionPrefixSet.get(urnPrefix)),
              action);
      Future<Boolean> future = executorService.submit(authenticationCallable);
      futures.add(future);
    }

    for (Future<Boolean> future : futures) {
      try {
        if (!future.get()) {
          return false;
        }
      } catch (InterruptedException e) {
        throw createSNAAException(e.getMessage());
      } catch (ExecutionException e) {
        throw createSNAAException(e.getMessage());
      }
    }

    return true;
  }
    @Override
    public void run() {
      FeedMedia media = DBReader.getFeedMedia(DownloadService.this, request.getFeedfileId());
      if (media == null) {
        throw new IllegalStateException("Could not find downloaded media object in database");
      }
      boolean chaptersRead = false;
      media.setDownloaded(true);
      media.setFile_url(request.getDestination());

      // Get duration
      MediaMetadataRetriever mmr = null;
      try {
        mmr = new MediaMetadataRetriever();
        mmr.setDataSource(media.getFile_url());
        String durationStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
        media.setDuration(Integer.parseInt(durationStr));
        if (BuildConfig.DEBUG) Log.d(TAG, "Duration of file is " + media.getDuration());
      } catch (NumberFormatException e) {
        e.printStackTrace();
      } catch (RuntimeException e) {
        e.printStackTrace();
      } finally {
        if (mmr != null) {
          mmr.release();
        }
      }

      if (media.getItem().getChapters() == null) {
        ChapterUtils.loadChaptersFromFileUrl(media);
        if (media.getItem().getChapters() != null) {
          chaptersRead = true;
        }
      }

      try {
        if (chaptersRead) {
          DBWriter.setFeedItem(DownloadService.this, media.getItem()).get();
        }
        DBWriter.setFeedMedia(DownloadService.this, media).get();
        if (!DBTasks.isInQueue(DownloadService.this, media.getItem().getId())) {
          DBWriter.addQueueItem(DownloadService.this, media.getItem().getId()).get();
        }
      } catch (ExecutionException e) {
        e.printStackTrace();
        status =
            new DownloadStatus(
                media,
                media.getEpisodeTitle(),
                DownloadError.ERROR_DB_ACCESS_ERROR,
                false,
                e.getMessage());
      } catch (InterruptedException e) {
        e.printStackTrace();
        status =
            new DownloadStatus(
                media,
                media.getEpisodeTitle(),
                DownloadError.ERROR_DB_ACCESS_ERROR,
                false,
                e.getMessage());
      }

      saveDownloadStatus(status);
      sendDownloadHandledIntent();

      numberOfDownloads.decrementAndGet();
      queryDownloadsAsync();
    }
  @SuppressWarnings({"unchecked", "boxing", "rawtypes"})
  private DiscoveryManager getRemoteProxyToDiscoveryManager(final String ip, final int port) {

    final String address = ip + ":" + port;
    final int timeout = 500; // 500 ms RemoteAPIImpl if need detailed version...

    this.logger.status("remoteproxytodiscovery/start", "ip", ip, "port", port);

    final DiscoveryManager newClient =
        Proxies.newClient(
            EXPORT_NAME, address, getClass().getClassLoader(), DiscoveryManager.class);

    // Execute collection asynchronously (TODO: cache pool usage could be improved)
    final ExecutorService cachePool =
        Executors.newCachedThreadPool(RemoteDiscoveryImpl.threadFactory);
    final ExecutorCompletionService<String> ecs = new ExecutorCompletionService(cachePool);
    final Future<String> future =
        ecs.submit(
            new Callable<String>() {

              public String call() throws Exception {
                return AccessController.doPrivileged(
                    new PrivilegedAction<String>() {
                      public String run() {
                        return newClient.ping(667) == 667 ? "OK" : null;
                      }
                    });
              }
            });

    // Wait at most half a second (TODO: Make this configurable)
    try {
      final String string = future.get(timeout, TimeUnit.MILLISECONDS);
      if (string == null) return null;

      return newClient;
    } catch (final InterruptedException e) {
      this.logger.status("remoteproxytodiscovery/exception/interrupted", "message", e.getMessage());
      e.printStackTrace();
    } catch (final ExecutionException e) {
      this.logger.status(
          "remoteproxytodiscovery/exception/executionexception", "message", e.getMessage());
    } catch (final TimeoutException e) {
      this.logger.status(
          "remoteproxytodiscovery/exception/timeoutexception", "message", e.getMessage());
    } catch (final SecurityException e) {
      this.logger.status(
          "remoteproxytodiscovery/exception/securityexception", "message", e.getMessage());
      e.printStackTrace();
    } finally {
      AccessController.doPrivileged(
          new PrivilegedAction<Object>() {
            public Object run() {
              future.cancel(true);
              cachePool.shutdownNow();
              return null;
            }
          });

      this.logger.status("remoteproxytodiscovery/end");
    }

    this.logger.status("remoteproxytodiscovery/end");
    return null;
  }