Skip to content

Commit

Permalink
Merge pull request #791 from caelum/ot-gh789-downloadbuilder
Browse files Browse the repository at this point in the history
Adding support for Download builder
  • Loading branch information
garcia-jj committed Sep 12, 2014
2 parents 9738325 + 7b4d4fb commit f6d9de5
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package br.com.caelum.vraptor.observer.download;

import static com.google.common.base.Objects.firstNonNull;
import static java.util.Objects.requireNonNull;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;

import javax.enterprise.inject.Vetoed;

/**
* A Builder to create a proper instance for {@link Download} class.
*
* @author Otávio S Garcia
* @since 4.1.0
*/
@Vetoed
public final class DownloadBuilder {

/**
* Creates an instance for build a {@link FileDownload}.<br>
*
* <code>
* Download download = DownloadBuilder.of(myFile)
* .withFileName("resume.txt") // optional, default is file.getName()
* .withContentType("text/plain") // optional, null will sent as octet/stream
* .downloadable() // optional, default is inline content
* .build();
* </code>
*
* @param file The input file.
* @throws NullPointerException If the {@code file} argument is {@code null}
*/
public static FileDownloadBuilder of(File file) {
return new FileDownloadBuilder(file);
}

/**
* Creates an instance for build a {@link InputStreamDownload}.<br>
*
* <code>
* Download download = DownloadBuilder.of(myInputStream)
* .withFileName("resume.txt") // optional
* .withContentType("text/plain") // optional, null will sent as octet/stream
* .downloadable() // optional, default is inline content
* .withSize(100L) // optional
* .build();
* </code>
*
* @param file The input InputStream to process.
* @throws NullPointerException If the {@code input} argument is {@code null}
*/
public static InputStreamDownloadBuilder of(InputStream input) {
return new InputStreamDownloadBuilder(input);
}

/**
* Creates an instance for build a {@link ByteArrayDownload}.<br>
*
* <code>
* Download download = DownloadBuilder.of(myFile)
* .withFileName("resume.txt") // optional
* .withContentType("text/plain") // optional, null will sent as octet/stream
* .downloadable() // optional, default is inline content
* .build();
* </code>
*
* @param file The input byte array.
* @throws NullPointerException If the {@code input} argument is {@code null}
*/
public static ByteArrayDownloadBuilder of(byte[] input) {
return new ByteArrayDownloadBuilder(input);
}

public static abstract class AbstractDownloadBuilder<T> {
protected String fileName;
protected String contentType;
protected boolean doDownload;

public T withFileName(String fileName) {
this.fileName = fileName;
return (T) this;
}

public T withContentType(String contentType) {
this.contentType = contentType;
return (T) this;
}

public T downloadable() {
this.doDownload = true;
return (T) this;
}
}

public static class FileDownloadBuilder extends AbstractDownloadBuilder<FileDownloadBuilder> {
private final File file;

public FileDownloadBuilder(File file) {
this.file = requireNonNull(file, "File can't be null");
}

public FileDownload build() throws FileNotFoundException {
fileName = firstNonNull(fileName, file.getName());
return new FileDownload(file, contentType, fileName, doDownload);
}
}

public static class InputStreamDownloadBuilder extends AbstractDownloadBuilder<InputStreamDownloadBuilder> {
private final InputStream input;
private long size;

public InputStreamDownloadBuilder(InputStream input) {
this.input = requireNonNull(input, "InputStream can't be null");
}

public InputStreamDownloadBuilder withSize(long size) {
this.size = size;
return this;
}

public InputStreamDownload build() {
return new InputStreamDownload(input, contentType, fileName, doDownload, size);
}
}

public static class ByteArrayDownloadBuilder extends AbstractDownloadBuilder<ByteArrayDownloadBuilder> {
private final byte[] buff;

public ByteArrayDownloadBuilder(byte[] buff) {
this.buff = requireNonNull(buff, "byte[] can't be null");
}

public ByteArrayDownload build() {
return new ByteArrayDownload(buff, contentType, fileName, doDownload);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void write(HttpServletResponse response) throws IOException {

private File checkFile(File file) throws FileNotFoundException {
if (!file.exists()) {
throw new FileNotFoundException("File " + file.getName() + "doesn't exists");
throw new FileNotFoundException("File " + file.getName() + " doesn't exists");
}

return file;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

Expand All @@ -28,14 +29,17 @@
import javax.servlet.http.HttpServletResponse;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import br.com.caelum.vraptor.observer.download.ByteArrayDownload;

public class ByteArrayDownloadTest {

@Rule
public ExpectedException thrown = ExpectedException.none();

private byte[] bytes;
private @Mock HttpServletResponse response;
private ServletOutputStream socketStream;
Expand All @@ -45,7 +49,7 @@ public class ByteArrayDownloadTest {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);

bytes = new byte[] { (byte) 0 };
bytes = new byte[] { (byte) 0x0 };
this.outputStream = new ByteArrayOutputStream();

this.socketStream = new ServletOutputStream() {
Expand Down Expand Up @@ -83,4 +87,21 @@ public void shouldUseHeadersToHttpResponse() throws IOException {
verify(response, times(1)).setHeader("Content-type", "type");
assertArrayEquals(bytes, outputStream.toByteArray());
}

@Test
public void builderShouldThrowsExceptionIfInputDataIsNull() throws Exception {
thrown.expect(NullPointerException.class);

DownloadBuilder.of((byte[]) null).build();
}

@Test
public void testConstructWithDownloadBuilder() throws Exception {
Download fd = DownloadBuilder.of(bytes).withFileName("file.txt")
.withContentType("text/plain").downloadable().build();
fd.write(response);

verify(response).setHeader("Content-Length", String.valueOf(bytes.length));
verify(response).setHeader("Content-disposition", "attachment; filename=file.txt");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,30 @@

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import br.com.caelum.vraptor.observer.download.FileDownload;

public class FileDownloadTest {

@Rule
public ExpectedException thrown = ExpectedException.none();

@Rule
public TemporaryFolder folder = new TemporaryFolder();

private File file;
private byte[] bytes;
private @Mock HttpServletResponse response;
Expand All @@ -52,7 +59,7 @@ public void setUp() throws Exception {
bytes = new byte[] { (byte) 0 };
outputStream = new ByteArrayOutputStream();

file = File.createTempFile("test", "vraptor");
file = folder.newFile();

try (FileOutputStream fileStream = new FileOutputStream(file)) {
fileStream.write(bytes);
Expand All @@ -77,11 +84,6 @@ public void setWriteListener(WriteListener writeListener) {
when(response.getOutputStream()).thenReturn(socketStream);
}

@After
public void tearDown() {
file.delete();
}

@Test
public void shouldFlushWholeFileToHttpResponse() throws IOException {
FileDownload fd = new FileDownload(file, "type");
Expand All @@ -99,4 +101,31 @@ public void shouldUseHeadersToHttpResponse() throws IOException {
verify(response, times(1)).setHeader("Content-disposition", "inline; filename=x.txt");
assertArrayEquals(bytes, outputStream.toByteArray());
}

@Test
public void builderShouldThrowsExceptionIfFileDoesntExists() throws Exception {
thrown.expect(FileNotFoundException.class);
thrown.expectMessage("File picture.jpg doesn't exists");

DownloadBuilder.of(new File("/path/that/doesnt/exists/picture.jpg")).build();
}

@Test
public void builderShouldUseNameArgument() throws Exception {
Download fd = DownloadBuilder.of(file).withFileName("file.txt")
.withContentType("text/plain").downloadable().build();
fd.write(response);

verify(response).setHeader("Content-Length", String.valueOf(file.length()));
verify(response).setHeader("Content-disposition", "attachment; filename=file.txt");
}

@Test
public void builderShouldUseFileNameWhenNameNotPresent() throws Exception {
Download fd = DownloadBuilder.of(file).withContentType("text/plain").build();
fd.write(response);

verify(response).setHeader("Content-Length", String.valueOf(file.length()));
verify(response).setHeader("Content-disposition", "inline; filename=" + file.getName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,19 @@
import javax.servlet.http.HttpServletResponse;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import br.com.caelum.vraptor.observer.download.InputStreamDownload;

public class InputStreamDownloadTest {

@Rule
public ExpectedException thrown = ExpectedException.none();

private InputStream inputStream;
private byte[] bytes;
private @Mock HttpServletResponse response;
Expand Down Expand Up @@ -87,4 +92,21 @@ public void shouldUseHeadersToHttpResponse() throws IOException {
assertArrayEquals(bytes, outputStream.toByteArray());
}

@Test
public void builderShouldThrowsExceptionIfInputStreamIsNull() throws Exception {
thrown.expect(NullPointerException.class);

DownloadBuilder.of((InputStream) null).build();
}

@Test
public void testConstructWithDownloadBuilder() throws Exception {
Download fd = DownloadBuilder.of(inputStream).withFileName("file.txt")
.withSize(bytes.length)
.withContentType("text/plain").downloadable().build();
fd.write(response);

verify(response).setHeader("Content-Length", String.valueOf(bytes.length));
verify(response).setHeader("Content-disposition", "attachment; filename=file.txt");
}
}
12 changes: 12 additions & 0 deletions vraptor-site/content/en/docs/download-and-upload.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@
}
~~~

##Using DownloadBuilder

`DownloadBuilder` is a class to help you to create instances for `Download`, using a fluent interface. To create a `FileDownload` you can write this code:

~~~
#!java
FileDownload download = DownloadBuilder.of(myFile)
.withFileName("resume.txt") // optional, default is File.getName()
.withContentType("text/plain") // optional, null will not sent
.downloadable() // optional, default is inline content
.build();
~~~

##Upload

Expand Down
12 changes: 12 additions & 0 deletions vraptor-site/content/pt/docs/download-e-upload.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@
}
~~~

##Using DownloadBuilder

`DownloadBuilder` é uma classe útil para ajudar você a criar instâncias da classe `Download`, usando uma interface fluente. Para criar uma instância de um `FileDownload` você pode escrever o seguinte código:

~~~
#!java
FileDownload download = DownloadBuilder.of(meuArquivo)
.withFileName("curriculo.txt") // opcional, o padrão é File.getName()
.withContentType("text/plain") // optional, não será enviado se nulo
.downloadable() // opcional, o padrão é inline content
.build();
~~~

##Upload

Expand Down

0 comments on commit f6d9de5

Please sign in to comment.