/**
   * Returns an input stream for reading the specified remote resource.<br>
   *
   * @param name the resource name
   * @return an input stream for reading the resource, or {@code null} if the resource could not be
   *     found
   */
  protected InputStream getRemoteResourceAsStream(String name) {
    try {
      log.fine("Trying to load resource " + name + " from the class provider.");

      ClassLoaderRequest request = new ClassLoaderRequest(name);
      ClassLoaderResponse response = (ClassLoaderResponse) mq.requestResponse(request, id);
      ClassLoaderOffer offer = response.getAcceptedOffer();

      log.fine("As a response to loading " + name + " received offer " + offer);

      // If a remote resource was found, store it in the cache and return it
      if (offer != null && offer.hasFiles()) {
        cache.registerOffer(offer);
        useOffer(null, offer);
        String fileLocation = cache.getFileLocation(name, offer.getName());

        // we could have got null because of wrong platform-dependent
        // separator used by user. Let's try again with fixed one.
        // (linux can use windows-style separators for escaping,
        // therefore it's not a good idea to always replace them)
        if (fileLocation == null)
          fileLocation =
              cache.getFileLocation(
                  name.replace('/', File.separatorChar).replace('\\', File.separatorChar),
                  offer.getName());

        if (fileLocation != null) {
          return new FileInputStream(fileLocation);
        } else
          log.warning(
              "The received offer does not contain the file requested or the file could not be stored: cache returned NULL");
      }
    } catch (JMSException | TimeoutException e) {
      throw new JCloudScaleException(e);
    } catch (FileNotFoundException e) {
      // Ignore
    }
    return null;
  }
  /**
   * Loads class and all additional required information from the client.
   *
   * @param className The name of the class to load
   * @return The Class object of the required class.
   */
  private Class<?> loadClassData(String className) {
    long start = System.nanoTime();
    try {
      //
      // Creating request
      //
      ClassLoaderRequest request = createRequestFromCache(className);

      if (request == null) { // this class is not registered in cache
        log.fine(
            String.format(
                "Class %s cannot be found in cache, requesting code from the client.", className));
        request = new ClassLoaderRequest(className);
      } else
        log.fine(
            String.format(
                "Class %s exists in cache in a %s offer(s). Verifying if client has the same code.",
                className, request.getOffers().length));

      ClassLoaderResponse response = (ClassLoaderResponse) mq.requestResponse(request, id);

      if (response == null) {
        log.severe(
            "Client sent null response for class " + className + ". Something is wrong out there.");
        return null;
      }

      ClassLoaderOffer acceptedOffer = response.getAcceptedOffer();

      if (acceptedOffer == null
          || (!acceptedOffer.hasFiles()
              && !acceptedOffer
                  .hasName())) { // client does not like the stuff we sent and did not provide
                                 // anything instead. Shit, we're screwed.
        if (request.getOffers() == null || request.getOffers().length == 0)
          log.severe(
              String.format(
                  "Client did not provide code for class %s and no cached version available.",
                  className));
        else
          log.severe(
              String.format(
                  "Client did not provide code for class %s and did not selected cached version.",
                  className));

        return null;
      }

      if (!acceptedOffer
          .hasFiles()) // can't check whether the name is specified as it can be cache item update.
      { // no code from client, but offer is selected
        String selectedOffer = acceptedOffer.getName();
        log.fine(String.format("Client selected offer %s for class %s.", selectedOffer, className));
        return useOffer(className, request.getOfferByName(selectedOffer));
      } else { // client provided code.
        log.fine(
            String.format(
                "Client provided code for class %s within %s files. saving it to cache and using it.",
                className, acceptedOffer.getFiles().length));

        // updating the offer
        cache.registerOffer(acceptedOffer);

        // merging 2 offers together and using it.(alternative would be to create offer again from
        // cache, but not if NoCache is used)
        ClassLoaderOffer proposedOffer = request.getOfferByName(acceptedOffer.getName());
        if (proposedOffer != null) updateOfferFiles(acceptedOffer, proposedOffer.getFiles());

        return useOffer(className, acceptedOffer);
      }
    } catch (Exception e) {
      log.severe("Failed to load class " + className + ": " + e.toString());
      e.printStackTrace();
      return null;
    } finally {
      String msg =
          "REMOTE CLASS LOADER: loading of "
              + className
              + " took "
              + (System.nanoTime() - start) / 1000000
              + "ms.";
      log.fine(msg);
    }
  }
  /** Ensures that all additional files that this file requires are loaded. */
  private void ensureAdditionalClassesLoaded(Class<?> clazz) {
    if (!cache.isClassLoadedFromCache(clazz)) return;

    if (!hasFileDependencyAnnotation(clazz)) return;

    // we're here if annotation is present. We have to detect if all files are loaded.
    //
    // getting items where this class is registered.
    //
    String[] items = cache.getItemNames(clazz.getName());

    // finding the item that we used to instantiate this class.
    String usedItem = null;
    if (items != null)
      for (String item : items)
        if (usedOffers.contains(item)) {
          usedItem = item;
          break;
        }
    //
    // checking if files for this item were loaded.
    //
    ClassLoaderRequest request = null;
    if (usedItem != null) {
      String[] classesWithFiles = cache.getClassesWithFiles(usedItem);
      if (classesWithFiles != null && Arrays.asList(classesWithFiles).contains(clazz.getName()))
        return; // everything is fine, we loaded all necessary stuff.

      request =
          new ClassLoaderRequest(
              clazz.getName(), new ClassLoaderOffer[] {cache.createOfferByItemName(usedItem)});
    } else {
      log.info(
          "Class "
              + clazz.getName()
              + " has FileDependency annotation, but we could not detect the offer that this class was taken from. Asking from the client.");
      request = new ClassLoaderRequest(clazz.getName());
    }

    // files were not loaded. asking client for the data for our class.
    // Client will have to include files.
    try {
      ClassLoaderResponse response = (ClassLoaderResponse) mq.requestResponse(request, id);
      ClassLoaderOffer acceptedOffer = response.getAcceptedOffer();

      // updating the offer
      if (acceptedOffer.hasFiles()) {
        cache.registerOffer(acceptedOffer);
        // unpacking and using it.
        // we don't need class, just files. but let's do the whole sequence...
        useOffer(null, acceptedOffer);
      } else
        log.info(
            "Trying to load missing dependent files of class "
                + clazz.getName()
                + " we received offer without files. Nothing to load.");
    } catch (JMSException | TimeoutException e) {
      log.severe(
          "Failed to load additional files for class "
              + clazz.getName()
              + " . Exception occured: "
              + e);
      return;
    }
  }