001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.rest.helper; 014 015import static org.apache.juneau.internal.CollectionUtils.*; 016import static org.apache.juneau.internal.IOUtils.*; 017 018import java.io.*; 019import java.util.*; 020 021import org.apache.juneau.*; 022import org.apache.juneau.http.*; 023import org.apache.juneau.http.annotation.*; 024 025/** 026 * Represents the contents of a byte stream file with convenience methods for adding HTTP response headers. 027 * 028 * <p> 029 * <br>These objects can to be returned as responses by REST methods. 030 * 031 * <p> 032 * <l>StreamResources</l> are meant to be thread-safe and reusable objects. 033 * <br>The contents of the request passed into the constructor are immediately converted to read-only byte arrays. 034 * 035 * <p> 036 * Instances of this class can be built using {@link StreamResourceBuilder}. 037 * 038 * <h5 class='section'>See Also:</h5> 039 * <ul> 040 * <li class='link'>{@doc juneau-rest-server.RestMethod.StreamResource} 041 * </ul> 042 */ 043@Response 044public class StreamResource implements Streamable { 045 046 private final MediaType mediaType; 047 private final byte[][] contents; 048 private final Map<String,Object> headers; 049 050 /** 051 * Creates a new instance of a {@link StreamResourceBuilder} 052 * 053 * @return A new instance of a {@link StreamResourceBuilder} 054 */ 055 public static StreamResourceBuilder create() { 056 return new StreamResourceBuilder(); 057 } 058 059 /** 060 * Constructor. 061 * 062 * @param mediaType The resource media type. 063 * @param headers The HTTP response headers for this streamed resource. 064 * @param contents 065 * The resource contents. 066 * <br>If multiple contents are specified, the results will be concatenated. 067 * <br>Contents can be any of the following: 068 * <ul> 069 * <li><code><jk>byte</jk>[]</code> 070 * <li><code>InputStream</code> 071 * <li><code>Reader</code> - Converted to UTF-8 bytes. 072 * <li><code>File</code> 073 * <li><code>CharSequence</code> - Converted to UTF-8 bytes. 074 * </ul> 075 * @throws IOException 076 */ 077 public StreamResource(MediaType mediaType, Map<String,Object> headers, Object...contents) throws IOException { 078 this.mediaType = mediaType; 079 080 this.headers = immutableMap(headers); 081 082 this.contents = new byte[contents.length][]; 083 for (int i = 0; i < contents.length; i++) { 084 Object c = contents[i]; 085 if (c == null) 086 this.contents[i] = new byte[0]; 087 else if (c instanceof byte[]) 088 this.contents[i] = (byte[])c; 089 else if (c instanceof InputStream) 090 this.contents[i] = readBytes((InputStream)c, 1024); 091 else if (c instanceof File) 092 this.contents[i] = readBytes((File)c); 093 else if (c instanceof Reader) 094 this.contents[i] = read((Reader)c).getBytes(UTF8); 095 else if (c instanceof CharSequence) 096 this.contents[i] = ((CharSequence)c).toString().getBytes(UTF8); 097 else 098 throw new IOException("Invalid class type passed to StreamResource: " + c.getClass().getName()); 099 } 100 } 101 102 /** 103 * Get the HTTP response headers. 104 * 105 * @return 106 * The HTTP response headers. 107 * <br>An unmodifiable map. 108 * <br>Never <jk>null</jk>. 109 */ 110 @ResponseHeader("*") 111 public Map<String,Object> getHeaders() { 112 return headers; 113 } 114 115 @ResponseBody 116 @Override /* Streamable */ 117 public void streamTo(OutputStream os) throws IOException { 118 for (byte[] b : contents) 119 os.write(b); 120 os.flush(); 121 } 122 123 @ResponseHeader("Content-Type") 124 @Override /* Streamable */ 125 public MediaType getMediaType() { 126 return mediaType; 127 } 128}