En tapestry el proceso no es muy complicado, tan solo deberemos devolver un objeto que implemente la interfaz StreamResponse en el manejador de evento («event handler») que procesa la petición.
Aquí el código de parte de un manejador de evento que construye y devuelve un objeto que implementa la interfaz StreamResponse:
Una posible implementación de la clase StreamResponse podría ser la siguiente que podríamos reutilizar siempre que tengamos que devolver un archivo:
Si estuviésemos devolviendo un archivo de texto y si el cliente acepta compresión gzip según las cabeceras que nos ha enviado en la petición (Accept-encoding: gzip) podríamos devolver el resultado comprimido con este formato ahorrado ancho de banda y muy posiblemente reduciendo el tiempo de descarga del archivo. Haciendo uso del servicio ResponseCompressionAnalyzer y del método isGZipSupported podemos averiguarlo de forma muy sencilla.
En la siguiente entrada de documentación sobre Apache Tapestry puedes encontrar más entradas, ejemplos y documentación.
Para finalizar, decir que la forma indicada en este ejemplo para convertir el OutputStream al InputStream que necesita el objeto StreamResponse puede ser mejorado con el uso de un PipedInputStream. La manera indicada en el ejemplo con el ByteArrayOuputStream obliga a mantener en memoria el contenido completo generado antes de ser enviado al cliente además de no empezar a enviarlo hasta que se genera por completo, si se trata de unos pocos MiB puede ser suficiente pero si son varias decenas la aplicación puede presentar algún problema en el uso de memoria.
Referencia:
http://tapestry.apache.org
http://betterexplained.com/articles/how-to-optimize-your-site-with-gzip-compression/