2
\$\begingroup\$

(See the first and initial iteration at RemoteFile in Java.)

I have added some new facilities for convenience. Now I have this in mind:

com.github.coderodde.utils.io.RemoteFileDownloadListener.java:

package com.github.coderodde.utils.io;

/**
 * This interface specifies an application programming interface for 
 * {@link RemoteFile} downloading.
 * 
 * @author Rodion "rodde" Efremov
 * @version 1.6 (Mar 15, 2020) ~ initial version.
 * @since 1.6 (Mar 15, 2020)
 */
public interface RemoteFileDownloadListener {

    public void onStart();
    public void onReady();
}

com.github.coderodde.utils.io.RemoteFile.java:

package com.github.coderodde.utils.io;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * This class implements a downloadable remote file.
 * 
 * @author Rodion "rodde" Efremov
 * @version 1.61 (Mar 15, 2020) 
 * @since 1.6 (Mar 14, 2020)
 */
public class RemoteFile {

    /**
     * The URL of the target remote file.
     */
    private final String url;

    /**
     * The list of remote file listeners listening on download progress.
     */
    private final List<RemoteFileDownloadListener> listeners = 
            new ArrayList<>();

    /**
     * Constructs a new {@code RemoteFile} object with given URL as a string.
     * 
     * @param url the URL of the target remote file.
     */
    public RemoteFile(String url) {
        this.url = Objects.requireNonNull(url, "The URL is null.");
    }

    public String getURl() {
        return this.url;
    }

    /**
     * Downloads the remote file to local disk.
     * 
     * @param path the path of the target file on the local disk.
     * 
     * @throws MalformedURLException if there are problems with URL.
     * 
     * @throws IOException if I/O fails.
     */
    public void download(String path) throws MalformedURLException,
                                             IOException,
                                             URISyntaxException {
        this.download(new File(path));
    }

    /**
     * Downloads the remote file to local disk..
     * 
     * @param file the target file object.
     * 
     * @throws MalformedURLException if there are issues with the URL.
     * 
     * @throws IOException if there are I/O issues.
     * 
     * @throws URISyntaxException if URI is bad.
     */
    public void download(File file) throws MalformedURLException,   
                                           IOException,
                                           URISyntaxException {

        for (RemoteFileDownloadListener listener : listeners) {
            listener.onStart();
        }

        InputStream inputStream = new URL(url).openStream();

        Files.copy(inputStream,
                   Path.of(file.getAbsolutePath()), 
                   StandardCopyOption.REPLACE_EXISTING);

        for (RemoteFileDownloadListener listener : listeners) {
            listener.onReady();
        }
    }

    /**
     * Adds a download listener.
     * 
     * @param listener the target listener.
     */
    public void addRemoteFileListener(RemoteFileDownloadListener listener) {
        this.listeners.add(
                Objects.requireNonNull(
                    listener, 
                    "The listener is null."));
    }

    /**
     * Removes a download listener.
     * 
     * @param listener the target listener.
     */
    public void removeRemoteFileListener(RemoteFileDownloadListener listener) {
        this.listeners.remove(listener);
    }
}

com.github.coderodde.utils.io.RemoteFileTest.java:

package com.github.coderodde.utils.io;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import org.junit.Test;

public class RemoteFileTest {

    @Test
    public void testDownloadedFileIsCreatedAfterDownload() 
            throws IOException, MalformedURLException, URISyntaxException {

        RemoteFile remoteFile =
                new RemoteFile("https://www.baeldung.com/java-download-file");

        remoteFile.addRemoteFileListener(new RemoteFileDownloadListener() {

            private long milliseconds;

            @Override
            public void onStart() {
                System.out.println("onStart");
                milliseconds = System.currentTimeMillis();
            }

            @Override
            public void onReady() {
                milliseconds = System.currentTimeMillis() - milliseconds;
                System.out.println(
                        "onReady. Milliseconds elapsed: " + milliseconds);
            }
        });

        remoteFile.download("C:\\baeldung.html");
    }
}

Critique request

I would like to hear any comments regarding my code.

\$\endgroup\$
3
  • \$\begingroup\$ Your test doesn't test, it just runs... \$\endgroup\$
    – tieskedh
    Mar 15, 2020 at 10:56
  • \$\begingroup\$ @tieskedh I know. The intent was to make sure that RemoteFile does not throw anything funny (unless there are connectivity problems). \$\endgroup\$
    – coderodde
    Mar 15, 2020 at 11:00
  • \$\begingroup\$ Ah, use org.junit.jupiter.api.Assertions.assertDoesNotThrow \$\endgroup\$
    – tieskedh
    Mar 15, 2020 at 11:55

1 Answer 1

2
\$\begingroup\$

in my opinion, the code is good, but I have some suggestions.

com.github.coderodde.RemoteFile class

com.github.coderodde.RemoteFile#download(java.io.File)

  1. The URISyntaxException in the method com.github.coderodde.RemoteFile#download(java.io.File) is never thrown and can be removed.

  2. In my opinion, the logic of this method should be extracted to an external object (composition). This will allow you to make unit tests without running the whole application, and it will allow you to add other types of remote files very easily.

com.github.coderodde.executors.Executor

public interface Executor {
   void execute(File file) throws IOException;
}

com.github.coderodde.executors.HttpExecutorImpl

public class HttpExecutorImpl implements Executor {
   private String url;

   public HttpExecutorImpl(String url) {
      this.url = url;
   }

   @Override
   public void execute(File file) throws IOException {

      InputStream inputStream = new URL(url).openStream();

      Files.copy(inputStream,
                 Path.of(file.getAbsolutePath()),
                 StandardCopyOption.REPLACE_EXISTING);
   }
}

Refactored code

com.github.coderodde.RemoteFile

public class RemoteFile {

   private final Executor executor;

   private final List<RemoteFileDownloadListener> listeners =
      new ArrayList<>();

   public RemoteFile(Executor executor) {
      this.executor = Objects.requireNonNull(executor, "The EXECUTOR is null.");
   }

   public void download(String path) throws
      IOException {
      this.download(new File(path));
   }

   public void download(File file) throws IOException {
      for (RemoteFileDownloadListener listener : listeners) {
         listener.onStart();
      }

      executor.execute(file);

      for (RemoteFileDownloadListener listener : listeners) {
         listener.onReady();
      }
   }

   public void addRemoteFileListener(RemoteFileDownloadListener listener) {
      this.listeners.add(
         Objects.requireNonNull(
            listener,
            "The listener is null."));
   }

   public void removeRemoteFileListener(RemoteFileDownloadListener listener) {
      this.listeners.remove(listener);
   }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.